The first point on the agenda we made up in the last weblog calls for using ABAP’s RTTI-classes to get a description of all the interfaces the proxy has to implement. To put this plan into action, basically all we have to do is retrieve an interface descriptor through CL_ABAP_TYPEDESCR=>DESCRIBE_BY_NAME for each of the supplied interface names and aggregate the results. The only difficulty arises from the fact that in ABAP interfaces can be composed of other interfaces. Our dynamic proxy class must also provide implementations for the methods of the included interfaces; hence we have to supplement the original list of interfaces with their component interfaces. Therefore, we need to query each interface descriptor for its component interfaces and then recursively retrieve their descriptors as well in a depth-first search.
The functionality outlined above is exposed through interface ZIF_DP_PROXY_DESCRIPTOR:
interface ZIF_DP_PROXY_DESCRIPTOR public. methods GET_DESCRIPTION returning VALUE(RS_DESCR) type PROXYDESCR raising ZCX_DP_EXCEPTION. endinterface.
It turns out that we can simplify our remaining tasks considerably if we bring the list of interface descriptors resulting from our efforts into another form, flattening and streamlining it so it just itemizes the information relevant for the generation of the dynamic proxy class. This is the purpose of type ZIF_DP_PROXY_DESCRIPTOR=>PROXYDESCR that we use for the return parameter of method ZIF_DP_PROXY_DESCRIPTOR~GET_DESCRIPTION
types: begin of PROXYDESCR, INTERFACES type ABAP_INTFDESCR_TAB, METHODS type ABAP_METHDESCR_TAB, EVENTS type ABAP_EVNTDESCR_TAB, end of PROXYDESCR.
Consistent with our deliberations in the last blog, this proxy descriptor structure does not make provisions for the interfaces’ attributes; however, all other elements including its events are present.
Interface ZIF_DP_PROXY_DESCRIPTOR is implemented by class ZCL_DP_PROXY_DESCRIPTOR whose definition is shown below:
class ZCL_DP_PROXY_DESCRIPTOR definition public create public. public section. interfaces ZIF_DP_PROXY_DESCRIPTOR. methods CONSTRUCTOR importing !IT_INTFNAMES type ZIF_DP_PROXY_MANAGER=>INTFNAME_TAB !IT_EXCL_INTF type ZIF_DP_PROXY_MANAGER=>INTFNAME_TAB optional. protected section. data MS_PROXYDESCR type ZIF_DP_PROXY_DESCRIPTOR=>PROXYDESCR. data MT_EXCL_INTF type ZIF_DP_PROXY_MANAGER=>INTFNAME_TAB. data MT_INTFDESCR type ZIF_DP_PROXY_DESCRIPTOR=>INTFDESCR_TAB. data MT_INTFNAMES type ZIF_DP_PROXY_MANAGER=>INTFNAME_TAB. methods BUILD_PROXYDESCR. methods RESOLVE_INTFNAMES raising ZCX_DP_EXCEPTION. methods TRAVERSE_INTFDESCR importing !IR_INTFDESCR type ZIF_DP_PROXY_DESCRIPTOR=>INTFDESCR_REF raising ZCX_DP_EXCEPTION. endclass.
The list of interface names is passed to the constructor in parameter IT_INTFNAMES; in the optional parameter IT_EXCL_INTF a second list can be supplied that contains the names of interfaces to be excluded from the proxy descriptor structure. This mechanism provides a (generalized) means to filter out interfaces like ZIF_DP_PROXY which the proxy class already implements internally.
The retrieval of the interface metadata is jointly accomplished via the methods RESOLVE_INTFNAMES and TRAVERSE_INTFDESCR, with the former looping over the list of supplied interface names calling the latter which in turn carries out the depth-first descriptor traversal we talked about earlier. The resulting list of CL_ABAP_INTFDESCR instances is stored in class variable MT_INTFDESCR.
method CONSTRUCTOR. MT_INTFNAMES = IT_INTFNAMES. MT_EXCL_INTF = IT_EXCL_INTF. sort MT_INTFNAMES. sort MT_EXCL_INTF. endmethod. method RESOLVE_INTFNAMES. data LR_TYPEDESCR type ref to CL_ABAP_TYPEDESCR. data LR_INTFDESCR type ZIF_DP_PROXY_DESCRIPTOR=>INTFDESCR_REF. data LR_EXCEPTION type ref to CX_ROOT. field-symbols type ABAP_INTFNAME. try. loop at MT_INTFNAMES assigning . CL_ABAP_TYPEDESCR=>DESCRIBE_BY_NAME( exporting P_NAME = receiving P_DESCR_REF = LR_TYPEDESCR exceptions others = 1 ). if SY-SUBRC <> 0. raise exception type ZCX_DP_INVALID_INTF_EXCEPTION exporting NAME = . endif. LR_INTFDESCR ?= LR_TYPEDESCR. TRAVERSE_INTFDESCR( LR_INTFDESCR ). endloop. catch CX_SY_MOVE_CAST_ERROR into LR_EXCEPTION. raise exception type ZCX_DP_INVALID_INTF_EXCEPTION exporting PREVIOUS = LR_EXCEPTION. endtry. endmethod. method TRAVERSE_INTFDESCR. data LR_INTFDESCR type ZIF_DP_PROXY_DESCRIPTOR=>INTFDESCR_REF. data LV_INTFNAME type ABAP_INTFNAME. field-symbols type ABAP_INTFDESCR. * filter excluded interfaces LV_INTFNAME = IR_INTFDESCR->GET_RELATIVE_NAME( ). read table MT_EXCL_INTF from LV_INTFNAME transporting no fields. check SY-SUBRC <> 0. read table MT_INTFDESCR from IR_INTFDESCR transporting no fields. * depth first search: if descriptor is in table, so * are its constituents -> nothing more to do check SY-SUBRC <> 0. if IR_INTFDESCR->INTF_KIND = CL_ABAP_INTFDESCR=>INTFKIND_NESTED. loop at IR_INTFDESCR->INTERFACES assigning . LR_INTFDESCR = IR_INTFDESCR->GET_INTERFACE_TYPE( -NAME ). TRAVERSE_INTFDESCR( LR_INTFDESCR ). endloop. endif. append IR_INTFDESCR to MT_INTFDESCR. endmethod.
The list of CL_ABAP_INTFDESCR instances is subsequently transformed into our proxy descriptor structure in method BUILD_PROXYDESCR. Please note that in this step we preserve the relationship between the interfaces and their components by prefixing the latter with their respective interface selector.
method BUILD_PROXYDESCR. data LR_DESCR type ZIF_DP_PROXY_DESCRIPTOR=>INTFDESCR_REF. data LS_INTFDESCR type ABAP_INTFDESCR. data LS_METHDESCR type ABAP_METHDESCR. data LS_EVNTDESCR type ABAP_EVNTDESCR. loop at MT_INTFDESCR into LR_DESCR. LS_INTFDESCR-NAME = LR_DESCR->GET_RELATIVE_NAME( ). append LS_INTFDESCR to MS_PROXYDESCR-INTERFACES. loop at LR_DESCR->METHODS into LS_METHDESCR. concatenate LS_INTFDESCR-NAME '~' LS_METHDESCR-NAME into LS_METHDESCR-NAME. append LS_METHDESCR to MS_PROXYDESCR-METHODS. endloop. loop at LR_DESCR->EVENTS into LS_EVNTDESCR. concatenate LS_INTFDESCR-NAME '~' LS_EVNTDESCR-NAME into LS_EVNTDESCR-NAME. append LS_EVNTDESCR to MS_PROXYDESCR-EVENTS. endloop. endloop. * ensure defined order of components sort MS_PROXYDESCR-INTERFACES. sort MS_PROXYDESCR-METHODS. sort MS_PROXYDESCR-EVENTS. endmethod.
Using the methods presented so far as building blocks, the implementation of method ZIF_DP_PROXY_DESCRIPTOR~GET_DESCRIPTION is then straight forward:
method ZIF_DP_PROXY_DESCRIPTOR~GET_DESCRIPTION. if MS_PROXYDESCR is INITIAL. RESOLVE_INTFNAMES( ). BUILD_PROXYDESCR( ). endif. RS_DESCR = MS_PROXYDESCR. endmethod.
Now that we have the interfaces’ metadata brought into a suitable form, we can start thinking about how to construct the dynamic proxy class implementation from it. This will be the topic of the next blog in this series.