Updates
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.
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.
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:
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.
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
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.
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.
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.
The report performs the following actions for you
The result of this process can be checked using ADT and using transaction /o/IWBEP/CP_ADMIN.
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:
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.
*&---------------------------------------------------------------------*
*& 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.|.
*&---------------------------------------------------------------------*
*& 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.|.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
34 | |
17 | |
16 | |
15 | |
11 | |
9 | |
8 | |
8 | |
8 | |
7 |