This is a test message to test the length of the message box.
Login
ABAP Cloud Reusable Components
Created by Software-Heroes

ABAP Cloud - Reusable Components

1026

What points should you consider when developing reusable components in ABAP Cloud? Here we look at various examples.



In this article, we look at working across software components and the challenges associated with using these components.

 

Introduction

Every system typically has a handful of components that are reused during development. Such central components are usually provided and maintained centrally. Such components include, for example, a facade for the application log, templates and services for sending emails, parameter tables, or various configurations. In the ABAP Cloud area, we use various software components to separate the components of our applications.

We have summarized why we use many TIER-1 software components (SWC) in this article. This involves the deliberate separation of code and DDIC elements to bring more structure and clean encapsulation to the system. Finally, we consider which objects we share with others.

 

Scenario

In this case, we use two classes and an interface. One class is a reuse component, and the other class implements some methods and has an interface where types are defined that we test in our scenario. The artifacts' structure is as follows.

 

 

Release

Since we are working with a reusable component and this is located in another software component, we must define a C1 Contract and thus enter into a stability contract. The object thus becomes our own released API in the system. If we want to use the class without sharing, we get the following error message.

 

In the next step, we should define our C1 Contract on the class.

 

Project Explorer

If you select the object in the Project Explorer and right-click to open the context menu, you will find an action in the lower area for suitable objects.

 

Properties

Once the object is open, you can switch to the "Properties" view and find the "API State" under the corresponding contract information. You can then access the standard release flow using the "+" button.

 

Flow

You will then go through the standard release flow for the objects. It is important that the C1 contract is used for the use of the object and that the "Use in Cloud Development" setting is set so that we can use the object within ABAP Cloud.

 

Finally, the entry is transferred to the transport, where some table entries are transported for release. The whole thing is summarized under the object type "APIS".

 

Data type

What about the general use of data types in cross-components? In this case, we assume that the type is located outside the Re-Use component in the calling SWC.

 

Preparation

To do this, we create a data type in the ZIF_BS_DEMO_SWC_USE interface, which we want to have populated in our cross-component. The type consists of different fields with different data types.

TYPES charlike TYPE c LENGTH 25.
TYPES packed   TYPE p LENGTH 15 DECIMALS 2.

TYPES: BEGIN OF dummy,
         number    TYPE i,
         char      TYPE charlike,
         string    TYPE string,
         packed    TYPE packed,
         timestamp TYPE utclong,
       END OF dummy.

TYPES dummys TYPE STANDARD TABLE OF dummy WITH EMPTY KEY.

 

Reference

In this scenario, we pass the data type into the method as a reference. To do this, we define our reuse method as follows:

METHODS table_reference
  IMPORTING !data TYPE REF TO data.

 

In the method implementation, we first need a field symbol to assign the reference. In the next step, we would then assign the data to the field symbol to return it.

METHOD table_reference.
  FIELD-SYMBOLS <table> TYPE STANDARD TABLE.

  ASSIGN data->* TO <table>.

  DATA(parts) = get_default_parts( ).
  <table> = CORRESPONDING #( parts ).
ENDMETHOD.

 

However, this method does not work because a SY-SUBRC of 4 is already set during ASSIGN. We currently have no access to the type from the interface.

 

Changing

In the next attempt, we use a Changing parameter to pass the table into the method from outside and fill it there.

METHODS table_changing
  CHANGING !data TYPE ANY TABLE.

 

The method implementation is quite simple; we simply create our dummy data and assign it to the Changing parameter using CORRESPONDING.

METHOD table_changing.
  DATA(parts) = get_default_parts( ).
  data = CORRESPONDING #( parts ).
ENDMETHOD.

 

This variant works so far, and the correspondingly filled fields reach the caller. In the example, you'll also find the same example for the topic EXPORTING; this scenario also works.

 

Dynamic

In the last example, we pass the type dynamically as a string and expect the routine to return the data as a generated reference from the type.

METHODS table_with_type
  IMPORTING type_name   TYPE string
  RETURNING VALUE(data) TYPE REF TO data.

 

During implementation, we dynamically create our data reference using the supplied type, assign the reference to the new field symbol, and pass the data.

METHOD table_with_type.
  CREATE DATA data TYPE (type_name).
  ASSIGN data->* TO FIELD-SYMBOL(<data>).

  DATA(parts) = get_default_parts( ).
  <data> = CORRESPONDING #( parts ).
ENDMETHOD.

 

In this case, we receive a dump immediately because the CREATE doesn't work. However, the message indicates that the type is unknown. With dynamic assignment, the type cannot be checked first, and therefore the message is not as precise.

 

Solution

In principle, you can also equip the ZIF_BS_DEMO_SWC_USE interface with a C1 release, but the interface would then be released throughout the entire system and could also be used by others, which could lead to dependencies. The cleanest approach would therefore be to create a "Software Component Relation." There, we release our components for the SWC of the cross-components (access permission).

If we now execute all methods of our class again, all procedures will work. Currently, however, the Software Component Relations are only available in the ABAP Environment and the S/4HANA Cloud Public Edition and will most likely be available in 2025.

 

Application Log

In the next example, we'll look at creating an application log. In most cases, we use a facade for the application log, since even with the new interfaces, transferring messages still requires a relatively large amount of code, as does initialization.

 

Scenario

To do this, we define a method in our cross-component that receives a table of messages and then stores them in an application log.

METHODS save_messages
  IMPORTING !messages TYPE messages
            !header   TYPE REF TO if_bali_header_setter OPTIONAL.

 

The implementation of the method is quite long, so here's a brief explanation. We create an Application Log object and then a corresponding header if none was passed. We then transfer the header to our Log object, save the message, and finally call SAVE to persist the messages to the log.

METHOD save_messages.
  DATA(application_log) = cl_bali_log=>create( ).

  DATA(local_header) = header.
  IF local_header IS INITIAL.
    local_header = cl_bali_header_setter=>create( object      = 'ZBS_APPL_REUSE'
                                                  subobject   = 'TEST'
                                                  external_id = CONV #( xco_cp=>uuid( )->value ) ).
  ENDIF.

  local_header->set_expiry( expiry_date       = CONV d( cl_abap_context_info=>get_system_date( ) + 7 )
                            keep_until_expiry = abap_true ).
  application_log->set_header( local_header ).

  LOOP AT messages INTO DATA(message).
    application_log->add_item( cl_bali_message_setter=>create_from_bapiret2( message ) ).
  ENDLOOP.

  cl_bali_log_db=>get_instance( )->save_log( application_log ).
ENDMETHOD.

 

The Application Log object "ZBS_APPL_REUSE" used in this case is located in our calling component, i.e., the application that uses the cross-component. For simplicity, we have hard-coded the object.

 

Error

If we now execute the method, an error occurs when creating the header in the method, and our routine aborts. When debugging, you will notice that the standard checks whether our Application Log object has been released. This means our component cannot create the header and therefore cannot save the log.

 

Solution

In this example, there are two solutions that can help us achieve our goal:

  1. Creating the header (CL_BALI_HEADER_SETTER) in the calling component and passing it to the Save method.
  2. Using Software Component Relations to release all objects for the cross-component components.

 

Adaptation

If you want to adapt shared APIs, this is generally possible, but you will also be prompted with a security message. when editing.

 

Therefore, you should follow the standard rules for changing artifacts.

 

Complete example

Here you can find all the resources shown in the article again; we have omitted GitHub this time.

 

Interface

The interface provides a type.

INTERFACE zif_bs_demo_swc_use
  PUBLIC.

  TYPES charlike TYPE c LENGTH 25.
  TYPES packed   TYPE p LENGTH 15 DECIMALS 2.

  TYPES: BEGIN OF dummy,
           number    TYPE i,
           char      TYPE charlike,
           string    TYPE string,
           packed    TYPE packed,
           timestamp TYPE utclong,
         END OF dummy.

  TYPES dummys TYPE STANDARD TABLE OF dummy WITH EMPTY KEY.
ENDINTERFACE.

 

Classes

The class should consume the cross-component.

CLASS zcl_bs_demo_swc_use DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.
    INTERFACES zif_bs_demo_swc_use.

  PRIVATE SECTION.
    METHODS call_method
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS call_with_changing
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS call_with_reference
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS call_for_reference
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS call_with_exporting
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS call_application_log
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.


CLASS zcl_bs_demo_swc_use IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    call_method( out ).
    call_with_changing( out ).
    call_with_reference( out ).
    call_for_reference( out ).
    call_with_exporting( out ).
    call_application_log( out ).
  ENDMETHOD.


  METHOD call_method.
    DATA(dummy_data) = VALUE zif_bs_demo_swc_use=>dummys(
        ( number = 1 char = 'one' string = `First field` packed = '1.11' timestamp = utclong_current( ) )
        ( number = 2 char = 'two' string = `Second field` packed = '2.22' timestamp = utclong_current( ) ) ).

    out->write( dummy_data ).
  ENDMETHOD.


  METHOD call_with_changing.
    DATA changed_data TYPE zif_bs_demo_swc_use=>dummys.

    DATA(reuse) = NEW zcl_bs_demo_swc_reuse( ).

    reuse->table_changing( CHANGING data = changed_data ).

    out->write( changed_data ).
  ENDMETHOD.


  METHOD call_with_reference.
    DATA changed_data TYPE zif_bs_demo_swc_use=>dummys.

    DATA(reuse) = NEW zcl_bs_demo_swc_reuse( ).

    reuse->table_reference( data = REF #( changed_data ) ).

    out->write( changed_data ).
  ENDMETHOD.


  METHOD call_for_reference.
    DATA(reuse) = NEW zcl_bs_demo_swc_reuse( ).

    DATA(reference) = reuse->table_with_type( type_name = 'ZIF_BS_DEMO_SWC_USE=>DUMMYS' ).

    out->write( reference->* ).
  ENDMETHOD.


  METHOD call_with_exporting.
    DATA changed_data TYPE zif_bs_demo_swc_use=>dummys.

    DATA(reuse) = NEW zcl_bs_demo_swc_reuse( ).

    reuse->table_exporting( IMPORTING data = changed_data ).

    out->write( changed_data ).
  ENDMETHOD.


  METHOD call_application_log.
    DATA(messages) = VALUE zcl_bs_demo_swc_reuse=>messages( id = 'ZDUMMY'
                                                            ( type = 'S' number = '001' )
                                                            ( type = 'W' number = '002' ) ).

*    DATA(header) = cl_bali_header_setter=>create( object      = 'ZBS_APPL_REUSE'
*                                                  subobject   = 'TEST'
*                                                  external_id = CONV #( xco_cp=>uuid( )->value ) ).

    DATA(reuse) = NEW zcl_bs_demo_swc_reuse( ).

    reuse->save_messages( messages = messages
*                          header   = header
                          ).

    out->write( messages ).
  ENDMETHOD.
ENDCLASS.

 

The class represents our overarching component.

CLASS zcl_bs_demo_swc_reuse DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    TYPES messages TYPE STANDARD TABLE OF bapiret2 WITH EMPTY KEY.

    METHODS table_changing
      CHANGING !data TYPE ANY TABLE.

    METHODS table_reference
      IMPORTING !data TYPE REF TO data.

    METHODS table_with_type
      IMPORTING type_name   TYPE string
      RETURNING VALUE(data) TYPE REF TO data.

    METHODS table_exporting
      EXPORTING !data TYPE ANY TABLE.

    METHODS save_messages
      IMPORTING !messages TYPE messages
                !header   TYPE REF TO if_bali_header_setter OPTIONAL.

  PRIVATE SECTION.
    TYPES: BEGIN OF part,
             number    TYPE i,
             string    TYPE string,
             timestamp TYPE utclong,
           END OF part.

    TYPES parts TYPE STANDARD TABLE OF part WITH EMPTY KEY.

    METHODS get_default_parts
      RETURNING VALUE(result) TYPE parts.
ENDCLASS.


CLASS zcl_bs_demo_swc_reuse IMPLEMENTATION.
  METHOD table_changing.
    DATA(parts) = get_default_parts( ).
    data = CORRESPONDING #( parts ).
  ENDMETHOD.


  METHOD table_reference.
    FIELD-SYMBOLS <table> TYPE STANDARD TABLE.

    ASSIGN data->* TO <table>.

    DATA(parts) = get_default_parts( ).
    <table> = CORRESPONDING #( parts ).
  ENDMETHOD.


  METHOD table_with_type.
    CREATE DATA data TYPE (type_name).
    ASSIGN data->* TO FIELD-SYMBOL(<data>).

    DATA(parts) = get_default_parts( ).
    <data> = CORRESPONDING #( parts ).
  ENDMETHOD.


  METHOD table_exporting.
    DATA(parts) = get_default_parts( ).
    data = CORRESPONDING #( parts ).
  ENDMETHOD.


  METHOD get_default_parts.
    RETURN VALUE parts( ( number = 13 string = `Thirteen` timestamp = utclong_current( ) )
                        ( number = 20 string = `Twenty`  ) ).
  ENDMETHOD.


  METHOD save_messages.
    DATA(application_log) = cl_bali_log=>create( ).

    DATA(local_header) = header.
    IF local_header IS INITIAL.
      local_header = cl_bali_header_setter=>create( object      = 'ZBS_APPL_REUSE'
                                                    subobject   = 'TEST'
                                                    external_id = CONV #( xco_cp=>uuid( )->value ) ).
    ENDIF.

    local_header->set_expiry( expiry_date       = CONV d( cl_abap_context_info=>get_system_date( ) + 7 )
                              keep_until_expiry = abap_true ).
    application_log->set_header( local_header ).

    LOOP AT messages INTO DATA(message).
      application_log->add_item( cl_bali_message_setter=>create_from_bapiret2( message ) ).
    ENDLOOP.

    cl_bali_log_db=>get_instance( )->save_log( application_log ).
  ENDMETHOD.
ENDCLASS.

 

Conclusion

The development of cross-components still works in the new world, but it is associated with certain risks and obstacles when it comes to a clean implementation in all cases. However, using the examples, you should be able to eliminate many problems.


Included topics:
ABAP CloudABAPReusableComponent
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


ABAP Cloud - Create XML

Category - ABAP

How can you actually create an XML in ABAP Cloud outside of transformations? In this article, we'll recreate an XML in detail.

03/28/2025

ABAP Cloud - Transport of the Software Component

Category - ABAP

What about the transport of software components in the ABAP Cloud? Do you also need the on-premise component in the test and production system?

03/11/2025

ABAP Cloud - CRV Update & TIER-3

Category - ABAP

How can you find the right API for your scenario in ABAP Cloud and what do you actually do with TIER-3 in your development? More information here.

02/25/2025

ABAP Cloud - Read XML

Category - ABAP

How can you read and process XML data relatively easily in ABAP Cloud? Let's look at an example and go through it step by step.

02/21/2025

ABAP Cloud - Clean Core (Scenarios)

Category - ABAP

In this article, let's take another look at the Clean Core architecture with ABAP Cloud, where it is used and where you can build your applications.

01/10/2025