Skip to Content

Using the results from the previous blog, we are ready to tackle the next point on our agenda, i.e. obtaining the canonical XML representation of the proxy descriptor structure that will later drive the code generation. But first we need to provide ourselves with some example interfaces to try things out with. The following random bunch, masquerading as part of some rather phoney simulation framework, will have to suffice for this purpose:

interface ZIF_DPX_CLOCK

  public.



  class-events TICKED

    exporting

      VALUE(INCREMENT) type FLOAT.



  methods MAKE_TICK.

endinterface.


interface ZIF_DPX_SIMULATED_OBJECT

  public.

  methods GET_NAME

    returning

      VALUE(RV_NAME) type STRING.

  class-methods GET_SPECIES

    returning

      VALUE(RV_SPECIES) type STRING.

  methods HANDLE_TICKED

    for event TICKED of ZIF_DPX_CLOCK

    importing

      !INCREMENT.

endinterface.


interface ZIF_DPX_GUINEA_PIG

  public.

  interfaces ZIF_DPX_SIMULATED_OBJECT.


  events HUNGRY.

  events THIRSTY.



  methods FEED

    importing

      !IV_CALORIES_PROVIDED type FLOAT

      !IV_FOOD_TYPE type STRING optional

    returning

      VALUE(RV_CALORIES_CONSUMED) type FLOAT

    raising

      ZCX_DPX_INDIGESTION.

  methods GET_AGE

    returning

     VALUE(RV_AGE) type FLOAT.

  methods WATER

    importing

      !IV_LITRES_PROVIDED type FLOAT

    returning

      VALUE(RV_LITRES_CONSUMED) type FLOAT

    exceptions

      DYSENTERY.

endinterface.

As you might note right away, I am no  expert on guinea pigs. However, we now have some methods, a number of events (even a static one!) and a composite interface to work with. Our present goal is to retrieve the XML document describing interface ZIF_DPX_GUINEA_PIG. This is trivially accomplished by the following code snippet:

data LR_DESCR type ref to ZIF_DP_PROXY_DESCRIPTOR.

data LT_INTFNAMES type ZIF_DP_PROXY_MANAGER=>INTFNAME_TAB.

data LV_INTFNAME type ABAP_INTFNAME.

data LS_PROXYDESCR type ZIF_DP_PROXY_DESCRIPTOR=>PROXYDESCR.

data LV_DESCR_XML type STRING.


start-of-selection.



  LV_INTFNAME = 'ZIF_DPX_GUINEA_PIG'.

  append LV_INTFNAME to LT_INTFNAMES.

  create object LR_DESCR type ZCL_DP_PROXY_DESCRIPTOR

    exporting

      IT_INTFNAMES = LT_INTFNAMES.

  LS_PROXYDESCR = LR_DESCR->GET_DESCRIPTION( ).



  call transformation ID

    source ABAP = LS_PROXYDESCR

    result xml LV_DESCR_XML.

Later, when we are ready to generate the actual proxy code, all we have to do is replace the canonical transformation ID with our own XSL transformation that contains the template code for the dynamic proxy class. For now, the ID transformation serves the useful purpose of giving us an idea how the source XML our XSLT will operate upon looks like. Executing the above code yields the following XML document:

\

One thing is probably worth pointing out: Only classical exceptions are listed in ABAP_METHDESCR and consequently appear in the above XML document. So far, I have found no way to retrieve information about the class based exceptions a method might throw at runtime (any suggestions?). This is not fatal for our present purpose, but bothersome nevertheless, since it deprives us of the possibility to deal with exceptions raised by the invocation handler but not declared by the calling method in our own way. Instead, the default behaviour will be enacted: Raising an undeclared exception in the invocation handler will result in a CX_SY_NO_HANDLER exception propagated to the proxy’s caller.

Figuring Out the Code

Before we can actually start generating code, quite obviously we have to know what code to generate. Therefore, we start by writing the relevant parts of a dynamic proxy class by hand and then generalize the resulting code into an XSLT template. In the process we will determine how to deal with issues like the handling of errors, the raising of events, etc.

To make our proxy class executable, we will need to put it into a subroutine pool or save it as a report, so we will create it as a local class (to generate a global class would be several orders of magnitude more complicated and is consequently left as an exercise to the reader). As justified in the first blog, our class will implement interface ZIF_DP_PROXY; in addition, we will derive it from an abstract base class ZCL_DP_PROXY_BASE that will serve to capture all the common code that can be shared between different proxies. Unfortunately, due to our earlier decision to define MR_STATIC_HANDLER as class attribute in ZIF_DP_PROXY, the interface can not already be implemented by ZCL_DP_PROXY_BASE because then all proxy class instances would share the same static invocation handler.

We end up with the following class structure:

/wp-content/uploads/2006/08/dynamic_proxy_impl_245986.gif

A minimalist implementation of class ZCL_DP_PROXY_BASE makes only provisions for holding a reference to its per-instance invocation handler in an attribute that gets initialized in the constructor:

class ZCL_DP_PROXY_BASE definition

  public

  abstract

  create public.


  public section.


    methods CONSTRUCTOR

      importing

        !IR_HANDLER type ref to ZIF_DP_INVOCATION_HANDLER.



  protected section.

    data MR_HANDLER type ref to ZIF_DP_INVOCATION_HANDLER.

endclass.   

class ZCL_DP_PROXY_BASE implementation.



  method CONSTRUCTOR.

    MR_HANDLER = IR_HANDLER.

  endmethod.  



endclass.

Maybe we will find more commonalities between different proxy classes later that can be factored out and pushed up into the base class. However, with ZCL_DP_PROXY_BASE as base class, a skeleton implementation of the dynamic proxy for interface ZIF_DPX_GUINEA_PIG takes on the form:

class lcl_proxy definition                                

  inheriting from zcl_dp_proxy_base.                      

  public section.                                         

    interfaces zif_dp_proxy.                              

    interfaces ZIF_DPX_GUINEA_PIG.                        

    interfaces ZIF_DPX_SIMULATED_OBJECT.                  

endclass.                                                 

class lcl_proxy implementation.                           

    method zif_dp_proxy~set_static_handler.               

      zif_dp_proxy~mr_static_handler = ir_static_handler. 

    endmethod.                                            

    method zif_dp_proxy~raise_event.                      

* TO DO                                                   

    endmethod.                                            

    method zif_dp_proxy~raise_static_event.               

* TO DO                                                   

    endmethod.                                            

    method ZIF_DPX_GUINEA_PIG~FEED.                       

* TO DO                                                   

    endmethod.                                            

    method ZIF_DPX_GUINEA_PIG~GET_AGE.                    

* TO DO                                                   

    endmethod.                                            

    method ZIF_DPX_GUINEA_PIG~WATER.                      

* TO DO                                                   

    endmethod.                                            

    method ZIF_DPX_SIMULATED_OBJECT~GET_NAME.             

* TO DO                                                   

    endmethod.                                            

    method ZIF_DPX_SIMULATED_OBJECT~GET_SPECIES.          

* TO DO                                                   

    endmethod.                                            

    method ZIF_DPX_SIMULATED_OBJECT~HANDLE_TICKED.        

* TO DO                                                   

    endmethod.                                            

endclass.     

Strictly speaking, there is no need to list interface ZIF_DPX_SIMULATED_OBJECT explicitly since it gets included via ZIF_DPX_GUINEA_PIG anyway;   owever, ABAP takes care of this redundancy for us so we can get away with just reproducing the list of interfaces contained in our proxy descriptor. Therefore, the above code can be generated by processing the proxy descriptor’s XML representation with the XSLT shown below:

Let’s have a short look at the anatomy of this XSL transformation:

The instruction <xsl:output \ method=”text” omit-xml-declaration=”yes”/> informs the XSLT processor the transformation’s output will not necessarily be  ell-formed XML and disables the escaping of  special characters like ‘>’ \ (&gt;) that would occur otherwise.

The instruction <xsl:strip-space elements=”*”/> strips all extraneous whitespace (i.e. every text node that consists entirely of whitespace characters) from the source XML and the XSLT itself. If we specifically need to insert a string of whitespace characters into the output document we can enclose it in <xsl:text> elements whose contents are exempt from this stripping process.

The template <xsl:template \ match=”ABAP”> is triggered by the <ABAP> tag in the source document. Here we include the definition and implementation of the proxy class into the output, calling additional templates through <xsl:apply-templates> to take care \ of its dynamic parts. The first template, <xsl:template \ match=”INTERFACES/item”>, inserts the list of implemented interfaces into the proxy’s definition, while the second one,   <xsl:template match=”METHODS/item”>, produces the corresponding method stubs.

This overview concludes the current instalment in this series of blogs. In the next blog we will focus on implementing the more demanding parts of the dynamic proxy class, for now conveniently hidden behind the ever popular “TO DO” comments.

To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

  1. Thomas Alexander Ritter
    Hi Achim,

    I already told you that I am working on something similar so this series is a must read for me.

    Love this line it’s definately a classic 🙂

    “to generate a global class would be several orders of magnitude more complicated and is consequently left as an exercise to the reader”

    I think you have to change this line to:

    “there is no need to list interface ZIF_DPX_SIMULATED_OBJECT explicitly since it gets included via ZIF_DPX_GUINEA_PIG (instead of: ZIF_DPX_SIMULATED_OBJECT) anyway”

    I will look into the exception problem but I doubt that I will have more luck than you.

    cheers
    Thomas

    (0) 
    1. Achim Bangert Post author
      Hi Thomas,

      thanks a lot for spotting the error, of course you’re completely right and I corrected the line accordingly. It’s gratifying to know there are readers (well, at least one) who aren’t too bored to follow the blog up to this point 😉

      Considering your commentary I presume that in your own framework you’re making use of the services in packages SEO* to generate a global class. How is this project coming along? I’d also be very interested to hear about any progress you’re able to make regarding the exception problem.

      Cheers
      Achim

      (0) 
      1. Thomas Alexander Ritter
        Hi Achim,

        in the first version my framework will only create proxies definitions in memory (nearly no need for seo* package stuff). This means that I am more focusing on stuff like how I can push the 30 subroutine pools limit as far as possible 🙂 I am also generating the code without XSL mostly because of lack of experience.

        The classes Thorsten mentions look really interesting cannot wait to give them a closer look.

        cheers
        Thomas

        (0) 
    2. Thorsten Franz
      Hello,

      one way to find the object-oriented exceptions used in a method’s signature is to use:

      Interface IF_OO_CLIF_COMPONENTS_FLAT
      Class CL_OO_CLASS_COMPONENTS_FLAT or CL_OO_INTERF_COMPONENTS_FLAT
      Attribute EXCEPTIONS

      Cheers,

      Thorsten

      (0) 

Leave a Reply