Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Andre_Fischer
Product and Topic Expert
Product and Topic Expert

Updates

    • 29.04.2020 - added picture and scenario description, list of applicable releases

 

    • 30.09.2023 - added clarification that this is a workaround that must be used SAP S/4HANA .

 

    • 28.11.2023 - changed the how to guide section. Now describe the use of the new service consumption model 2.0.

 

    • 25.03.2024 - added a second version of the report zaf_r_create_mpc_from_scm20 which is valid for SAP S/4 HANA 2023 systems since the signature of the class /iwbep/cl_cp_registry_config has changed in SAP S/4HANA 2023



Introduction


If you want to consume an OData service in your ABAP coding there is a nice feature available which is called OData Client Proxy that allows to develop ABAP code that consumes a (remote) OData service.

The OData Client itself is already available in SAP S/4HANA 1709 and thus also in later releases of SAP S/4HANA.

The tricky thing in using this useful feature in SAP S/4HANA so far is, that it requires you to build an appopriate OData V4 model provider class that fits to the OData service that you want to consume.

OData Client Proxy User Guide | SAP Help Portal

In SAP BTP, ABAP Environment your life as a developer is easier since here it is possible to generate a Service Consumption Model by importing the EDMX file of an OData V2 service. An OData Proxy Client can easily be instantiated based on such a Service Consumption Model.


If you want to call an Odata Service from within the SAP BTP, ABAP Environment you can check out the following tutorial.

 


Recently my colleague bernhard.grusie published a very detailed blog post about the new Service Consumption Model 2.0.

Service Consumption Model 2 for OData Client Proxy | SAP Blogs

where he mentioned that it is planned to make the Service Consumption Model 2.0 for future on premise releases as well.

Since the service consumption model 2.0 is thus not yet available for SAP S/4HANA on premise systems, I would like to describe in this blog post how you can make use of the new Service Consumption Model 2.0 already in older releases if you have access to an SAP BTP ABAP Environment System.

For the workaround I describe in the following you can also leverage the SAP BTP trial systems.


Workaround for on premise / private cloud SAP S/4HANA systems


As mentioned above, the problem when using the OData Client Proxy is, that it requires an OData V4 model which is conveniently created for you in SAP Cloud Platform, ABAP Environment when you create a Service Consumption Model.


Since creating an OData V4 model manually just from analyzing the $metadata document of the OData V2 service that you want to consume is a tedious and error-prone task I thought whether there are workarounds that can be used.

With the advent of the Service Consumption Model 2.0 a model class is being generated which is very similar to an OData V4 model provider class of the SAP Gateway framework.

With a few manual steps that I will describe in the remaining part of this blog post it is possible to create an OData V4 model provider class from the generated source code of an Service Consumption Model 2.0.

The way how to create an odata client proxy model which I will describe in this blog post is meant as a workaround that can be used by customers that are using an on premise or private cloud version of SAP S/4HANA where the Service Consumption Model 2.0 is not yet availble.

The steps you have to follow are the following:

    1. Create a service consumption model 2.0 in a SAP BTP, ABAP Environment system (e.g. a trial system).

 

    1. Copy the source code of the generated Service Consumption Model 2.0 class into a class with the same name in your on premise SAP S/4HANA System

 

    1. Adapt the code so that it becomes a valid OData V4 model provider class using the report zaf_r_create_mpc_from_scm20

 

    1. Register an OData Proxy CLient



Use case


The use case is the integration of any OData service punlished by other SAP systems that run on premise or in the cloud or other OData services.


In this blog I will describe how to call the OData service GWSAMPLE_BASIC that can reside either in another SAP S/4 HANA system or on the same system.

Applicable releases


The scenario described can be implemented in any recent SAP S/4HANA on premise release and also older SAP S/4HANA releases since the SAP Gateway Framework has been down ported as described in SAP Note 2512479 - SAP Gateway Foundation Support Package Stack Definition

 

How to section

 

Step 1 - Create a service consumption model


You have to create a service consumption model and a model class as described in the blog post of my colleague Bernhard Gruise.

In this case we will create a service consumption model to consume the demo OData service GWSAMPLE_BASIC.

 

Step 2 - Create a class in SAP S/4HANA


Create a class with the same name as the class that has been generated when creating the service consumption model 2.0 in step 1.

Copy the source code from the service consumption model class to the class in SAP S/4HANA.

As shown in the following screen shot  you will notice an error message that the class /iwbep/cl_v4_abs_pm_model_prov is not known.


 

Step 3 - Run the report zaf_r_create_mpc_from_scm20


In order to create a valid OData V4 model provider class out of the generated source code of our service consumption model 2.0 we have to perform several replacements.

Since performing all this replacements is a tedious and errorprone task I have created a report zaf_r_create_mpc_from_scm20 that reads the source code of the inactive class and provides you with the fixed source code.

The source code of this report can be found at the end of this blog post. The list of replacements is stored in the internal table replacement_list.

 

    1. Start report zaf_r_create_mpc_from_scm20 

 

    1. Select inactive class

 

    1. Execute report



The report performs the following actions for you

 

    1. It replaces code snippets so that the service consumption model 2.0 class becomes a valid OData V4 model provider class

 

    1. It activates the modified class

 

    1. It registers the model provider class as an OData Proxy Client



The result of this process can be checked using ADT and using transaction /o/IWBEP/CP_ADMIN.

Report result

 

 

Changed source code

 



Transaction /IWBEP/CP_ADMIN

 



Step 9 Test the OData Proxy Client locally


We can now test the OData Proxy Client locally in our SAP S/4HANA System since the service GWSAMPLE_BASIC is available in such systems.

 

CLASS zcl_my_odata_proxy DEFINITION

  PUBLIC

  FINAL

  CREATE PUBLIC .



  PUBLIC SECTION.

    INTERFACES if_oo_adt_classrun.

  PROTECTED SECTION.

  PRIVATE SECTION.

ENDCLASS.







CLASS zcl_my_odata_proxy IMPLEMENTATION.

  METHOD if_oo_adt_classrun~main.

    DATA: lt_salesorder    TYPE zaf_sc_gwsample_basic_20=>tyt_sales_order,

          lo_client_proxy  TYPE REF TO /iwbep/if_cp_client_proxy,

          lo_read_request  TYPE REF TO /iwbep/if_cp_request_read_list,

          lo_read_response TYPE REF TO /iwbep/if_cp_response_read_lst.



    DATA lv_relative_service_root TYPE string.



    lv_relative_service_root = '/sap/opu/odata/IWBEP/GWSAMPLE_BASIC/'.



    TRY.

        "throws an exception if service document cannot be read



        " Using SM59 destination for HTTP client object

*        cl_http_client=>create_by_destination(

*          EXPORTING

*            destination = 'LOCAL_HTTP_AF'

*          IMPORTING

*            client      = DATA(lo_http_client)

*          EXCEPTIONS

*            OTHERS      = 0 ).



        cl_http_client=>create_internal(

         IMPORTING

           client      = DATA(lo_http_client)

         ).

        IF sy-subrc <> 0.

          out->write( 'error create by http destination').

          EXIT.

        ENDIF.



        lo_client_proxy = /iwbep/cl_cp_client_proxy_fact=>create_v2_remote_proxy(

           io_http_client = lo_http_client

           is_proxy_model_key = VALUE #( repository_id = /iwbep/if_cp_registry_types=>gcs_repository_id-default

           proxy_model_id = to_upper( 'zaf_sc_gwsample_basic_20' )

           proxy_model_version = 0001 )

           iv_relative_service_root = lv_relative_service_root ).



        " 'SALESORDER' is the ABAP internal name of the entityset of the V4 model



        lo_read_request = lo_client_proxy->create_resource_for_entity_set( zaf_sc_gwsample_basic_20=>gcs_entity_set-sales_order_set )->create_request_for_read( ).

        lo_read_request->set_top( iv_top = 5 ).



        lo_read_response = lo_read_request->execute( ).



        " Retrieve the business data

        lo_read_response->get_business_data( IMPORTING et_business_data = lt_salesorder ).



        LOOP AT lt_salesorder INTO DATA(ls_salesorder).

          out->write( ls_salesorder ).

        ENDLOOP.



      CATCH /iwbep/cx_cp_remote INTO DATA(lx_cp_remote).

        " Error handling

        out->write( lx_cp_remote->get_longtext(  ) ).

      CATCH /iwbep/cx_gateway INTO DATA(lx_gateway).

        " Error Handling

        out->write( lx_gateway->get_longtext(  ) ).

    ENDTRY.



  ENDMETHOD.



ENDCLASS.

 


 

This will show the following result:

 


 

Source code of  zaf_r_create_mpc_from_scm20

Please note that you have the signature of class /iwbep/cl_cp_registry_config has changed from 2022 to 2023. As a result you will have to use one of the following listings depending on the release of your SAP S/4HANA system.

SAP S/4HANA 2023

 

*&---------------------------------------------------------------------*
*& Report zaf_r_create_mpc_from_scm20
*&---------------------------------------------------------------------*
*& Valid for SAP S/4HANA 2023
*&---------------------------------------------------------------------*
REPORT zaf_r_create_mpc_from_scm20.

PARAMETERS: cls_name LIKE seoclass-clsname.

DATA result TYPE i.

DATA search_for TYPE string.
DATA replace_by TYPE string.
FIELD-SYMBOLS <source_code_line> TYPE string.


TYPES: BEGIN OF replacement,
         search_for TYPE string,
         replace_by TYPE string,
       END OF replacement.

DATA replacement_list TYPE STANDARD TABLE OF replacement.

CONSTANTS scm20_super_class TYPE string VALUE '/iwbep/cl_v4_abs_pm_model_prov'.



replacement_list = VALUE #(
                       "change inheritance being used and method names
                       ( search_for = |INHERITING FROM /iwbep/cl_v4_abs_pm_model_prov|
                         replace_by = |INHERITING FROM /iwbep/cl_v4_abs_model_prov|   )
                       ( search_for = |METHODS /iwbep/if_v4_mp_basic_pm~define REDEFINITION.|
                         replace_by = |METHODS /iwbep/if_v4_mp_basic~define REDEFINITION.|   )
                       ( search_for = |METHOD /iwbep/if_v4_mp_basic_pm~define.|
                         replace_by = |METHOD /iwbep/if_v4_mp_basic~define.|   )
                       ( search_for = |/iwbep/if_v4_pm_|
                         replace_by = |/iwbep/if_v4_med_|   )
                       ( search_for = |/iwbep/if_v4_med_types=>ty_internal_name|
                         replace_by = |/iwbep/if_v4_med_types=>ty_e_med_internal_name|   )
                       ( search_for = |/iwbep/if_v4_med_types=>gcs_nav_multiplicity-|
                         replace_by = |/iwbep/if_v4_med_element=>gcs_med_nav_multiplicity-|   )
                       ( search_for = |lo_primitive_property->set_edm_type_v2( 'DateTime' ).|
                         replace_by = |cast /iwbep/if_v4_med_prim_type_cp( lo_primitive_property )->set_v2_edm_type( 'DateTime' ).|   )
                       ( search_for = |lo_primitive_property->set_scale_floating( ).|
                         replace_by = |"    lo_primitive_property->set_scale_floating( ).|   )
                       ( search_for = |iv_do_gen_prim_props|
                         replace_by = |iv_gen_prim_props|   )
                       ( search_for = |iv_do_gen_prim_prop_colls|
                         replace_by = |iv_gen_prim_prop_colls|   )
                       ( search_for = |iv_do_add_conv_to_prim_props|
                         replace_by = |iv_add_conv_to_prim_props|   )
                       ( search_for = |lo_parameter->set_is_nullable( ).|
                         replace_by = |"  lo_parameter->set_is_nullable( ).|   )
*                       ( search_for = | |
*                         replace_by = | |   )
                       ).




cls_name = to_upper( cls_name ).

SELECT SINGLE  * FROM i_abapobjectdirectoryentry INTO @DATA(obj_entry)
                           WHERE abapobject = @cls_name
                           .

DATA(package) = obj_entry-abappackage.

IF package IS INITIAL.
  WRITE : / |Class { cls_name } not found|.
  EXIT.
ENDIF.

DATA : proxy_model_id      TYPE /iwbep/if_cp_registry_types=>ty_proxy_model_id,
       proxy_model_version TYPE /iwbep/if_cp_registry_types=>ty_proxy_model_version.

proxy_model_id = cls_name.
proxy_model_version = 1.

DATA register_cp  TYPE REF TO /iwbep/cl_cp_registry_config.
register_cp = /iwbep/cl_cp_registry_config=>create( ).

TRY.
    IF register_cp->is_proxy_model_registered(
        iv_proxy_model_id      = proxy_model_id
        iv_proxy_model_version = proxy_model_version ).
      WRITE : / |Proxy model { proxy_model_id } version { proxy_model_version } is already registered|.
      EXIT.
    ENDIF.
  CATCH /iwbep/cx_gateway INTO DATA(lx_gateway).
    WRITE : / |Error checking { proxy_model_id } : { lx_gateway->get_text( ) } |.
    EXIT.
ENDTRY.


DATA(ref) = cl_oo_factory=>create_instance( )->create_clif_source( to_upper( cls_name ) ).
ref->get_source( IMPORTING source = DATA(source_code) ).

LOOP AT source_code INTO DATA(source_code_line).
  result = find( val = source_code_line sub  = replacement_list[ 1 ]-search_for case = abap_false ).
  IF result <> -1.
    EXIT.
  ENDIF.
ENDLOOP.

IF result = -1.
  WRITE : / | { cls_name } does not inherit from { scm20_super_class } |.
  EXIT.
ENDIF.

LOOP AT replacement_list INTO DATA(replacement).
  search_for = replacement-search_for.
  replace_by = replacement-replace_by.
  LOOP AT source_code ASSIGNING <source_code_line> .
    result = find( val = <source_code_line> sub  = search_for case = abap_false ).
    IF result <> -1.
      <source_code_line> = replace( val = <source_code_line>
                                    sub = search_for
                                    with = replace_by ).
    ENDIF.
  ENDLOOP.
ENDLOOP.
"output

TRY.
    ref->lock( ).
    ref->set_source( source = source_code ).
    ref->save( ).
    ref->unlock( ).
  CATCH  cx_oo_access_permission  INTO DATA(access_permission_exc).
    WRITE : / |error occured: { access_permission_exc->get_text(  ) }|.
    EXIT.
ENDTRY.

DATA objects TYPE STANDARD TABLE OF dwinactiv .

objects =  VALUE #( ( object = 'CLAS' obj_name = cls_name uname = sy-uname )
                       ).

CALL FUNCTION 'RS_WORKING_OBJECTS_ACTIVATE'
  TABLES
    objects                = objects
  EXCEPTIONS
    excecution_error       = 1
    cancelled              = 2
    insert_into_corr_error = 3
    OTHERS                 = 4.

IF sy-subrc <> 0.
  WRITE : / |error occured when activating class { cls_name }. SY-SUBRC = { sy-subrc } |.
  EXIT.
ENDIF.



TRY.
    register_cp->create_proxy_model(
      EXPORTING
        iv_proxy_model_id      = proxy_model_id
        iv_proxy_model_version = proxy_model_version
        iv_mpc_name            = cls_name
        iv_proxy_model_descr         = |Proxy model { proxy_model_id } |
        iv_package             = package
        iv_suppress_dialog     = abap_false ).

  CATCH /iwbep/cx_gateway INTO lx_gateway.
    WRITE : / |Error registering { proxy_model_id } : { lx_gateway->get_text( ) } |.
    EXIT.
ENDTRY.

WRITE : / |{ cls_name } registered as OData Proxy Client. |.

WRITE : / |run transaction /o/IWBEP/CP_ADMIN to check the result.|.

 

SAP S/HANA 2022 and earlier

 

 

*&---------------------------------------------------------------------*

*& Report zaf_r_create_mpc_from_scm20

*&---------------------------------------------------------------------*

*& Valid for SAP S/4HANA 2022 and earlier

*&---------------------------------------------------------------------*

REPORT zaf_r_create_mpc_from_scm20.



PARAMETERS: cls_name LIKE seoclass-clsname.



DATA result TYPE i.



DATA search_for TYPE string.

DATA replace_by TYPE string.

FIELD-SYMBOLS <source_code_line> TYPE string.





TYPES: BEGIN OF replacement,

         search_for TYPE string,

         replace_by TYPE string,

       END OF replacement.



DATA replacement_list TYPE STANDARD TABLE OF replacement.



CONSTANTS scm20_super_class TYPE string VALUE '/iwbep/cl_v4_abs_pm_model_prov'.







replacement_list = VALUE #(

                       "change inheritance being used and method names

                       ( search_for = |INHERITING FROM /iwbep/cl_v4_abs_pm_model_prov|

                         replace_by = |INHERITING FROM /iwbep/cl_v4_abs_model_prov|   )

                       ( search_for = |METHODS /iwbep/if_v4_mp_basic_pm~define REDEFINITION.|

                         replace_by = |METHODS /iwbep/if_v4_mp_basic~define REDEFINITION.|   )

                       ( search_for = |METHOD /iwbep/if_v4_mp_basic_pm~define.|

                         replace_by = |METHOD /iwbep/if_v4_mp_basic~define.|   )

                       ( search_for = |/iwbep/if_v4_pm_|

                         replace_by = |/iwbep/if_v4_med_|   )

                       ( search_for = |/iwbep/if_v4_med_types=>ty_internal_name|

                         replace_by = |/iwbep/if_v4_med_types=>ty_e_med_internal_name|   )

                       ( search_for = |/iwbep/if_v4_med_types=>gcs_nav_multiplicity-|

                         replace_by = |/iwbep/if_v4_med_element=>gcs_med_nav_multiplicity-|   )

                       ( search_for = |lo_primitive_property->set_edm_type_v2( 'DateTime' ).|

                         replace_by = |cast /iwbep/if_v4_med_prim_type_cp( lo_primitive_property )->set_v2_edm_type( 'DateTime' ).|   )

                       ( search_for = |lo_primitive_property->set_scale_floating( ).|

                         replace_by = |"    lo_primitive_property->set_scale_floating( ).|   )

                       ( search_for = |iv_do_gen_prim_props|

                         replace_by = |iv_gen_prim_props|   )

                       ( search_for = |iv_do_gen_prim_prop_colls|

                         replace_by = |iv_gen_prim_prop_colls|   )

                       ( search_for = |iv_do_add_conv_to_prim_props|

                         replace_by = |iv_add_conv_to_prim_props|   )

                       ( search_for = |lo_parameter->set_is_nullable( ).|

                         replace_by = |"  lo_parameter->set_is_nullable( ).|   )

*                       ( search_for = | |

*                         replace_by = | |   )

                       ).









cls_name = to_upper( cls_name ).



SELECT SINGLE  * FROM I_ABAPObjectDirectoryEntry INTO @DATA(obj_entry)

                           WHERE ABAPObject = @cls_name

                           .



DATA(package) = obj_entry-ABAPPackage.



IF package IS INITIAL.

  WRITE : / |Class { cls_name } not found|.

  EXIT.

ENDIF.



DATA : proxy_model_id      TYPE /iwbep/if_cp_registry_types=>ty_proxy_model_id,

       proxy_model_version TYPE /iwbep/if_cp_registry_types=>ty_proxy_model_version.



proxy_model_id = cls_name.

proxy_model_version = 1.



TRY.

    IF /iwbep/cl_cp_registry_config=>is_proxy_model_registered(

        iv_proxy_model_id      = proxy_model_id

        iv_proxy_model_version = proxy_model_version ).

      WRITE : / |Proxy model { proxy_model_id } version { proxy_model_version } is already registered|.

      EXIT.

    ENDIF.

  CATCH /iwbep/cx_gateway INTO DATA(lx_gateway).

    WRITE : / |Error checking { proxy_model_id } : { lx_gateway->get_text( ) } |.

    EXIT.

ENDTRY.





DATA(ref) = cl_oo_factory=>create_instance( )->create_clif_source( to_upper( cls_name ) ).

ref->get_source( IMPORTING source = DATA(source_code) ).



LOOP AT source_code INTO DATA(source_code_line).

  result = find( val = source_code_line sub  = replacement_list[ 1 ]-search_for case = abap_false ).

  IF result <> -1.

    EXIT.

  ENDIF.

ENDLOOP.



IF result = -1.

  WRITE : / | { cls_name } does not inherit from { scm20_super_class } |.

  EXIT.

ENDIF.



LOOP AT replacement_list INTO DATA(replacement).

  search_for = replacement-search_for.

  replace_by = replacement-replace_by.

  LOOP AT source_code ASSIGNING <source_code_line> .

    result = find( val = <source_code_line> sub  = search_for case = abap_false ).

    IF result <> -1.

      <source_code_line> = replace( val = <source_code_line>

                                    sub = search_for

                                    with = replace_by ).

    ENDIF.

  ENDLOOP.

ENDLOOP.

"output



TRY.

    ref->lock( ).

    ref->set_source( source = source_code ).

    ref->save( ).

    ref->unlock( ).

  CATCH  cx_oo_access_permission  INTO DATA(access_permission_exc).

    WRITE : / |error occured: { access_permission_exc->get_text(  ) }|.

    EXIT.

ENDTRY.



DATA objects TYPE STANDARD TABLE OF dwinactiv .



objects =  VALUE #( ( object = 'CLAS' obj_name = cls_name uname = sy-uname )

                       ).



CALL FUNCTION 'RS_WORKING_OBJECTS_ACTIVATE'

  TABLES

    objects                = objects

  EXCEPTIONS

    excecution_error       = 1

    cancelled              = 2

    insert_into_corr_error = 3

    OTHERS                 = 4.



IF sy-subrc <> 0.

  WRITE : / |error occured when activating class { cls_name }. SY-SUBRC = { sy-subrc } |.

  EXIT.

ENDIF.



TRY.

    /iwbep/cl_cp_registry_config=>create_proxy_model(

      EXPORTING

        iv_proxy_model_id      = proxy_model_id

        iv_proxy_model_version = proxy_model_version

        iv_mpc_name            = cls_name

        iv_proxy_model_descr         = |Proxy model { proxy_model_id } |

        iv_package             = package

        iv_suppress_dialog     = abap_false ).



  CATCH /iwbep/cx_gateway INTO lx_gateway.

    WRITE : / |Error registering { proxy_model_id } : { lx_gateway->get_text( ) } |.

    EXIT.

ENDTRY.



WRITE : / |{ cls_name } registered as OData Proxy Client. |.



WRITE : / |run transaction /o/IWBEP/CP_ADMIN to check the result.|.

 


 

17 Comments