Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
manwell77t
Explorer

Suppose you're calling a remote enabled function module and its signature parameter's data types don't exist in local data dictionary (for example because of different sap product such as SRM and R/3 for example). Thinking about a structure data type probably you don't have defined in your calling system data dictionary even the most of data elements used for defining each field. In order to pass importing parameters and receive result you must replicate signature data types. So, usually, data types are simply replicated on calling system; coding them (for example in a type group) or defining them in local data dictionary. For deeper structures with tens of fields could be a long and annoying job. But what if a structure data type defined on remote system, maybe because of an upgrade or because of a custom include, changes ? That generates a field displacement between local and remote structures that could even lead to a dump (or simply to a field misread). In that case you must realign local data types with remote ones. So, how to avoid definitely field displacement and dumps ? The only way to completely avoid any intervention is to dynamically build structure data types at runtime respecting remote definition.   Runtime generation of structures (and internal table) data types based on   structure data types defined in local dictionary is quite easy by means of cl_abap_structdescr and cl_abap_tabledescr classes. Method describe_by_name of class cl_abap_structdescr allows us to know "building details" of a   specific structure data type.


A quick example using structure BAPIRET2
* data declarations  

data: lo_struct type ref to cl_abap_structdescr,
lt_comp type cl_abap_structdescr=>component_table,
lo_run_st type ref to cl_abap_structdescr,
lo_run_tt type ref to cl_abap_tabledescr,
lo_struc type ref to data,
lo_ttype type ref to data.

field-symbols: <fs_table> type standard table,
<fs_line> type any.


* describe local structure
lo_struct ?= cl_abap_structdescr=>describe_by_name( 'BAPIRET2' ).


* get local structure components
lt_comp = lo_struct->get_components( ).

* create structure and table type descriptor
lo_run_st = cl_abap_structdescr=>create( lt_comp ).
lo_run_tt = cl_abap_tabledescr=>create( p_line_type = lo_struct
p_table_kind = cl_abap_tabledescr=>tablekind_std
p_unique = abap_false ).

* handle runtime data type by means of generic variables
create data: lo_struc type handle lo_run_st,
lo_ttype type handle lo_run_tt.

assign: lo_struc->* to <fs_line>,
lo_ttype->* to <fs_table>.

By means of standard function module 'DDIF_FIELDINFO_GET' (defined as remote   enabled and for sure present in both sap system) is possible to know building   details (field by field) of a specific structure data type. So, instead of   building our structure data type using details returned by the static method describe_by_name of cl_abap_structdescr  class we  could build it using abap predefined data types (because data element may not exist locally) by means of what 'DDIF_FIELDINFO_GET' returns in table parameter "dfies_tab".


That snippet (the code of my zcl_dyn_remote_type_builder=>get_elemdescr method) shows how to build structure components (so a descriptor, an instance of cl_abap_elemdescr) starting from knowledge of internal length, internal type and decimal places. That code was recently updated (thanks to my colleague fabrizio.gemma) for solving an INT1 and INT2 bug.



  data: lx_pir  type ref to cx_parameter_invalid_range.

data: lv_int2 type int2,
lv_int1 type int1.

try.

case i_inttype.

when cl_abap_elemdescr=>typekind_int.
result = cl_abap_elemdescr=>get_i( ).

when cl_abap_elemdescr=>typekind_int1.
result ?= cl_abap_elemdescr=>describe_by_data( p_data = lv_int1 ).

when cl_abap_elemdescr=>typekind_int2.
result ?= cl_abap_elemdescr=>describe_by_data( p_data = lv_int2 ).

when cl_abap_elemdescr=>typekind_float.
result = cl_abap_elemdescr=>get_f( ).

when cl_abap_elemdescr=>typekind_date.
result = cl_abap_elemdescr=>get_d( ).
when cl_abap_elemdescr=>typekind_packed.
result = cl_abap_elemdescr=>get_p( p_length = i_intlen
p_decimals = i_decimals ).

when cl_abap_elemdescr=>typekind_char.
result = cl_abap_elemdescr=>get_c( p_length = i_intlen ).

when cl_abap_elemdescr=>typekind_time.
result = cl_abap_elemdescr=>get_t( ).

when cl_abap_elemdescr=>typekind_num.
result = cl_abap_elemdescr=>get_n( p_length = i_intlen ).

when cl_abap_elemdescr=>typekind_hex.
result = cl_abap_elemdescr=>get_x( p_length = i_intlen ).

when cl_abap_elemdescr=>typekind_string.
result = cl_abap_elemdescr=>get_string( ).

when cl_abap_elemdescr=>typekind_xstring.
result = cl_abap_elemdescr=>get_xstring( ).

when others.

raise exception type zcx_dyn_remote_type_builder
exporting
textid = zcx_dyn_remote_type_builder=>no_inttype
inttype = i_inttype.

endcase.

catch cx_parameter_invalid_range into lx_pir.
raise exception lx_pir.

endtry.

and here is how to use it (fixing also a unicode bug found in previous version of code):



* data declaration

data: lo_elem type ref to cl_abap_elemdescr.

data: lt_fields type standard table of dfies,
ls_comp type abap_componentdescr,
ls_dfies type dfies,
ls_tmp_dfies type dfies,
ls_x030l type x030l,
lv_intlen type i,
lv_decimals type i,
lv_off type i,
lv_count type numc3.

data: lx_parameter_invalid_range type ref to cx_parameter_invalid_range.

* rfc destination check
if not i_rfcdest is initial.

call function 'RFC_CHECK_DESTINATION'
exporting
mydest = i_rfcdest
mytype = zcl_dyn_remote_type_builder=>rfctype3
exceptions
empty_destination = 1
invalid_logical_destination = 2
destination_with_special_char = 3
internal_destination_id = 4
empty_rfctype = 5
others = 6.

if sy-subrc ne 0.

raise exception type zcx_dyn_remote_type_builder
exporting
textid = zcx_dyn_remote_type_builder=>rfc_unreachable
rfcdest = i_rfcdest.

endif.

endif.

* get dictionary information from remote system
call function 'DDIF_FIELDINFO_GET' destination i_rfcdest
exporting
tabname = i_struct
importing
x030l_wa = ls_x030l
tables
dfies_tab = lt_fields
exceptions
not_found = 1
internal_error = 2
others = 3.

if sy-subrc ne 0.

raise exception type zcx_dyn_remote_type_builder
exporting
textid = zcx_dyn_remote_type_builder=>no_struc
struct = i_struct.

endif.

* component table builder
try.

* build structure field by field
loop at lt_fields into ls_dfies.

case ls_dfies-inttype.

when cl_abap_elemdescr=>typekind_char or
cl_abap_elemdescr=>typekind_date or
cl_abap_elemdescr=>typekind_time or
cl_abap_elemdescr=>typekind_num.

lv_intlen = ls_dfies-intlen / ls_x030l-unicodelg.

when others.

lv_intlen = ls_dfies-intlen.

endcase.

lv_decimals = ls_dfies-decimals.

* offset management with a dummy filed (if needed)
lv_off = ls_dfies-offset - ( ls_tmp_dfies-offset + ls_tmp_dfies-intlen ).

if lv_off gt 0.

lo_elem = cl_abap_elemdescr=>get_x( lv_off ).
ls_comp-type = lo_elem.
add 1 to lv_count.

concatenate zcl_dyn_remote_type_builder=>offset '_' lv_count into ls_comp-name.

append ls_comp to result.

free: lo_elem.

endif.

* field management by means of internal abap types
ls_comp-name = ls_dfies-fieldname.

* build element
lo_elem = zcl_dyn_remote_type_builder=>get_elemdescr( i_inttype = ls_dfies-inttype
i_intlen = lv_intlen
i_decimals = lv_decimals ).

* assign element
ls_comp-type = lo_elem.

append ls_comp to result.

ls_tmp_dfies = ls_dfies.

clear: ls_dfies.

endloop.

catch cx_parameter_invalid_range into lx_parameter_invalid_range.

raise exception lx_parameter_invalid_range.

endtry.

In this nugg you could see a concrete example of how it works.

I would like to special thank my colleague fabrizio.gemma for discovering and solving a bug involving datatype INT1 and INT2 on method GET_ELEM_DESCR of ZCL_DYN_REMOTE_TYPE_BUILDER class and hans.pettersson for discovering and solving a bug concerning substructure includes. The code shown two boxes above has been updated according to their suggestion: since there isn't any GET_* method in CL_ABAP_ELEMDESCR class for datatype INT1 and INT2 Fabrizio solved applying the DESCRIBE_BY_DATA method to declared variables of these types and casting the result; Hans solved the problem reviewing offset calculation logic and dummy field generation.


Starting from this point i extended remote runtime data-typing to handle also nested structures and nested internal tables (the nugg already includes these changes). For more details i refer you to my new blog.


The nugg contains:

  • class ZCL_DYN_REMOTE_TYPE_BUILDER whose static methods + CREATE_STRUCT_TYPE+ and CREATE_TABLE_TYPE build structype and tabletype object that a variable could handle at runtime.

  • exception class ZCX_DYN_REMOTE_TYPE_BUILDER (and message classZDYNTYPEBUILDER)

  • program ZDYNSTRUCTBUILDERDEMO that shows an example remote calling function module "BAPI_USER_GET_DETAIL" (supposing result structures aren't available locally)


Recently i spent a little bit of my free time to modernize my code, reviewing and rewriting it in a full object oriented way. I also improved it with additional features for both remote typing and querying.


The new nugg can be downloaded there. There's a program named zsrqldemo showing some code examples.

12 Comments