Quantcast
Channel: ABAP Development
Viewing all 948 articles
Browse latest View live

Embedding Flash content in SAP GUI

$
0
0

Embedding Flash content in SAP GUI

 

 

We as ABAP Developers are always interested in making SAP more and more interesting as well as take things to the extreme. In this post I will try to embed a Flash animation in the SAP GUI.

Embedding Flash animations, unlike images, cannot be done by uploading content in SWO0 and then using a function module to fetch the content. Instead, the content needs to be displayed in a container which will somehow support flash content. We know that flash content is easily displayed in web browsers and thus can be displayed in SAP GUI as well if we are able to create a container for HTML content. There is a simple report available in SAP which helps us create a container for HTML in SAP GUI. You can go to SE38 or SE80 to check the report SAPHTML_DEMO1. This is a good example of cl_gui_html_viewer.

The report is quite simple to understand for an ABAPer and we can copy the report and screens and then customize the container using screen painter.

Given below is an example on a demo I made for test purposes.

 

gif.gif

(link: http://cdn.makeagif.com/media/8-01-2013/Cjh2Qa.gif)


Here, instead of linking the container's HTML content to a web page, I linked it to the swf object in a local drive. However, the flash object can be placed in

banner.jpg

a web server or an FTP server so that the content is available to all users. You may also upload the swf file in the MIME repository of a BSP application from where you can directly access the file.

 

 

The benefits of embedding a flash animation in SAP are many. For example, the customer might be intrigued by the idea that there exists a dynamic content in a particular SAP transaction where the user can view a demo or presentation. Interactive Flash animations can also serve many more purposes and extends the capabilities of SAP.

 

I know that you would like to try this for yourself, so check the above mentioned demo report or the code below. To get the report working you need to draw a custom container named 'HTML' in screen 100 of the report and a use the menu painter to enable the back button.

 

 

 

 

 

 

 

 

 


 

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

*& Report  ZTEST_GAURAB_01

*&

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

*&

*&

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

 

 

REPORT ztest_gaurab_01 MESSAGE-ID z1.

 

 

DATA: html_control TYPE REF TO cl_gui_html_viewer,

      my_container TYPE REF TO cl_gui_custom_container,

      fcode LIKE sy-ucomm,

      edurl(2048),

      alignment TYPE i.

 

 

SET SCREEN 100.

 

 

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

*&      Module  STATUS_0100  OUTPUT

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

MODULE status_0100 OUTPUT.

  SET PF-STATUS 'TESTHTM1'.

  SET TITLEBAR '001'.

 

 

  IF my_container IS INITIAL.

 

 

    CREATE OBJECT my_container

        EXPORTING

            container_name = 'HTML'

        EXCEPTIONS

            others = 1.

    CASE sy-subrc.

      WHEN 0.

*

      WHEN OTHERS.

        RAISE cntl_error.

    ENDCASE.

  ENDIF.

 

 

  IF html_control IS INITIAL.

    CREATE OBJECT html_control

         EXPORTING

              parent    = my_container.

    IF sy-subrc NE 0.

      RAISE cntl_error.

    ENDIF.

 

 

    PERFORM load_home_page.

  ENDIF.

ENDMODULE.                             " STATUS_0100  OUTPUT

 

 

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

*&      Module  USER_COMMAND_0100  INPUT

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

MODULE user_command_0100 INPUT.

 

 

  CASE fcode.

    WHEN 'BACK'.

      IF NOT html_control IS INITIAL.

        CALL METHOD html_control->free.

        FREE html_control.

      ENDIF.

      IF NOT my_container IS INITIAL.

        CALL METHOD my_container->free

          EXCEPTIONS

            OTHERS = 1.

        IF sy-subrc <> 0.

*         MESSAGE E002 WITH F_RETURN.

        ENDIF.

        FREE my_container.

      ENDIF.

 

 

      LEAVE PROGRAM.

 

 

    WHEN OTHERS.

      CALL METHOD cl_gui_cfw=>dispatch.

 

 

  ENDCASE.

  CLEAR fcode.

ENDMODULE.                             " USER_COMMAND_0100  INPUT

 

 

* Homepage form

FORM load_home_page.

  DATA: doc_url(80).

 

 

    doc_url = 'U:\Desktop\banner.swf'.

    CALL METHOD html_control->show_url

         EXPORTING

              url       = doc_url.

 

 

ENDFORM.                               " LOAD_HOME_PAGE



Logging Objects into DB in OO Abap

$
0
0

When to log objects into DB?

 

Applications get bigger and bigger due to the evolving changes in requirements.

 

Scenario: You are working on a project where users enter data through screens and

you probably do BAPI/BADI calls or BDC calls.

Application holds very valuable data, sensitive.

You need to save each data as possible.

 

Logging is one way of doing this.

You can log the data into tables...

 

Business Scenario: This is a real world exampleç lets say you are working on a project where

users can be able to create many acct documents at a time, depending on the data users enter.

 

So you need to save each data, screen data, the internal tables, etc....

 

Lets say user has entered all the screen data and lots of business calculations behind the scene occur.

Smth went wrong during the process we need to see every data thats been entered

Murphys Law things that can go wrong will definetely go wrong.

 

How to  Approach?

 

Foundation: Object oriented world we should be living inso we need to answer all these questions in OO world.

 

Approach: we will need to wrap all the data in objects from the screen data to the data that we will need to analyze.

 

Lets say screen data is wrapped up as a z class which hold many details of the screen as well as the internal tales.

 

 

How to store object into DB?

 

How can we store object into the DB, we need a way to map object to a data type.

The possible way is storing the object as raw data of course eg rawstring.

 

 

How to convert objects into raw data

 

Of course the answer lies at the Serilazibility and the transformation of the object

IF_SERIALIZABLE_OBJECT is the interface that needs to be implemented!!

 

**transform the object

  CALL TRANSFORMATION ID_INDENT

     SOURCE OBJ = IO_OBJECT ( has to be serializable)

     RESULT XML LT_RESTAB.

 

Then we can convert much easily ...:)

 

 

Here the Transformation

 

Sample code for the Transformation:

 

class ZCL_GENERAL definition

   public

   create public .

 

*"* public components of class ZCL_GENERAL

*"* do not include other source files here!!!

public section.

 

   class-methods TRANSFORM_OBJECT_TO_RAWSTRING

     importing

       !IO_OBJECT type ref to IF_SERIALIZABLE_OBJECT

     returning

       value(RV_XSTRING) type XSTRING

     exceptions

       TRANSFORMATION_FAILED .


   class-methods TRANSFORM_RAWSTRING_TO_OBJECT

     importing

       value(IV_XSTRING) type XSTRING

     exporting

       !EO_OBJECT type ref to IF_SERIALIZABLE_OBJECT

     exceptions

       TRANSFORMATION_FAILED .

 

endclass.

 

 

Implementation

 

Transformatıon Sample Code: Notice that there is also compression applied!!

 

 

METHOD TRANSFORM_OBJECT_TO_RAWSTRING.

   types:

     line_t(4096) type x .

   types:

     table_t type standard table of line_t .


   DATA:LT_RESTAB TYPE table_t.

   FIELD-SYMBOLS: <LFS_RESTAB> LIKE LINE OF LT_RESTAB.

 

 

   CALL TRANSFORMATION ID_INDENT

     SOURCE OBJ = IO_OBJECT

     RESULT XML LT_RESTAB.

 

   IF LT_RESTAB IS NOT INITIAL.

     LOOP AT LT_RESTAB ASSIGNING <LFS_RESTAB>.

       CONCATENATE RV_XSTRING <LFS_RESTAB>

       INTO RV_XSTRING IN BYTE MODE.

     ENDLOOP.

 

**compresss here

     TRY.

         CALL METHOD CL_ABAP_GZIP=>COMPRESS_BINARY

           EXPORTING

             RAW_IN   = RV_XSTRING

           IMPORTING

             GZIP_OUT = RV_XSTRING.

       CATCH CX_PARAMETER_INVALID_RANGE .

       CATCH CX_SY_BUFFER_OVERFLOW .

       CATCH CX_SY_COMPRESSION_ERROR .

     ENDTRY.

 

   ELSE.

     RAISE  TRANSFORMATION_FAILED.

   ENDIF.

 

 

   ENDMETHOD.

 

We transformed the object into raw data!!

 

Now we need to recover the data back tooo from rawstring back to the object we saved!!

 

 

How to Recover

Notice that we use compression to save more data!!!

 

 

METHOD TRANSFORM_RAWSTRING_TO_OBJECT.

   DATA: LV_XSTRING_DECOMPRESSED TYPE XSTRING.

 

 

***Restore the object here

****first decompress the object

*now decompress

   CL_ABAP_GZIP=>DECOMPRESS_BINARY(

     EXPORTING

       GZIP_IN     = IV_XSTRING

     IMPORTING

       RAW_OUT     = LV_XSTRING_DECOMPRESSED

          ).

 

 

   " revert TRANSFORMATION

   CALL TRANSFORMATION ID_INDENT

     SOURCE XML LV_XSTRING_DECOMPRESSED

     RESULT OBJ = EO_OBJECT.

 

   IF NOT EO_OBJECT IS BOUND.

     RAISE TRANSFORMATION_FAILED.

   ENDIF.

 

ENDMETHOD.

 

 

 

 

Testing the Transformation

 

Here is a sample code to test the transformation!!!

 

METHOD TEST_TRANSFORM.

   DATA: LO_OBJECT TYPE REF TO ZCL_TEST,

         LO_OBJECT_RECOVERED TYPE REF TO ZCL_TEST,

         LO_OBJECT_SERILIAZABLE TYPE REF TO IF_SERIALIZABLE_OBJECT,

         LV_XSTRING_COMPRESSED TYPE XSTRING

        .

 

 

 

   CREATE OBJECT LO_OBJECT.

 

   LV_XSTRING_COMPRESSED =

   ZCL_AKSA_GENERAL=>TRANSFORM_OBJECT_TO_RAWSTRING(

   IO_OBJECT = LO_OBJECT

     ).

 

 

   ZCL_AKSA_GENERAL=>TRANSFORM_RAWSTRING_TO_OBJECT(

      EXPORTING

        IV_XSTRING            = LV_XSTRING_COMPRESSED

      IMPORTING

        EO_OBJECT  =   LO_OBJECT_SERILIAZABLE

     EXCEPTIONS

       TRANSFORMATION_FAILED = 1

          ).

   IF SY-SUBRC <> 0.

* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

*            WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.

   ENDIF.

 

 

***type cast

   LO_OBJECT_RECOVERED ?= LO_OBJECT_SERILIAZABLE.

 

 

*  CL_AUNIT_ASSERT=>ASSERT_EQUALS(

*      EXP                  = LO_OBJECT

*      ACT                  = LO_OBJECT_RECOVERED

**    MSG                  = MSG

**      LEVEL                = CRITICAL

**    TOL                  = TOL

**    QUIT                 = METHOD

**    IGNORE_HASH_SEQUENCE = ABAP_FALSE

*         ).

 

 

 

ENDMETHOD.

 

 

Notice the type casting from the serliazable object back to the object type!!!


Open XML edit document Contents

$
0
0

This is a screen cast showing  Word Open XML documents generation

from  SAP.

after the documents are generated,

there is a nice example of how to manual

editing some of the documents contents

using Open XML principles

such as OPC ( open packaging convention ).

it can give you a better understanding

of Open XML in SAP

and think of how can it help you

in  your daily work.

 

 

http://youtu.be/t8s_PYLHKns

 

 

Enjoy

 

Ronen Almog

Things that are cool to change for everyone and those you’d better don’t touch

$
0
0

<rant>

Dear SAP,

I have just upgraded my system and there are several things I would like to ask you about. There are things that would be very cool if you could change them for everyone, every single SAP system at some stage (via upgrades and for new installations too). But there are also things, that don’t help at all. They don’t help me at least. Of course, this is my opinion only, and I am more wondering about your reasons (please share it with us, we are listening), than a blind criticism.

 

No, thank you: CALL FUNCTION pattern in SE80

First of all the trigger for this blog: When I use the function module pattern in SE80 after the upgrade to Basis 7.31, the default message handling is gone. I could understand what might have been the reason: you want the developers to more active and instead of just leaving the MESSAGE code there as the default handling, you want the developers to actually do something, right? Well, ok, good idea. But the problem is that (if I am not mistaken), the default MESSAGE statement was there for so long, that thousands of developers (more like millions?) have gotten used to it, they have been designing their code with this “feature in mind” and last but not least they have been very efficient using this particular part of the pattern.

I was FAR MORE efficient before you changed this. Now, when I use the CALL FUNCTION pattern, I must go search for a section of code, where I have this message handling from the old times. I want it the old way, but the system took it away.

Did you run a Design thinking session first and that got you this idea? Well, the source of the idea is not that important for me. Did anyone think about the impact? That is far more important for me.

 

No, thank you: Warnings from the syntax check

Example number two: I am a tidy developer, I don’t like warnings in my code and that is why 99,9% of my code is warning free (in case you’re interested, that 0,1% of the code with warnings is shortening the real field length to a shorter length for SQL operations; then I am able to compare something that is 40 characters long field, but I know it only has 20 characters long value with a DB field that is 20 characters long by definition; if you wonder what is the reason for this, it is of course the fact that fields with same values are often defined differently and I don’t want to spend time on conversions).

Now, after the upgrade, all my code turns yellow, because of the funny warning that you’ve introduced, that one can hide with pragma called ##TOO_MANY_ITAB_FIELDS. For those who don’t know it yet: if you define your work-area as type MARA, but you only select MATNR field, you will get this warning telling you that your work-area is too big which is a waste of memory (I am guessing that is the reason here). In case of single field I can understand that, total waste. But how often we – normal average ABAPers – select half of the fields from a table (knowing that we don’t need the other fields and that is why we don’t ask for them, we are efficient with our SQLs by naming the fields we want, so why do you tell me the same thing with this new warning?) and to be fast (and spare ourselves the effort to define a local or even a DDIC type) we define the work-area as TYPE TABLE OF whatever we’re querying?

 

Screenshots follow, so we all are on the same code-page:

message.png

pragmas.png

 

I want my code to be “green”. I don’t want any warnings. I must go and get rid of these so that I can see the IMPORTANT warnings - those telling me PROBLEMS with my code. Maybe the intention is right, but honestly… I don’t appreciate this particular effort. If it must be there, because customer asked for it, ok. But give me a mean to turn it off. Is there such way? I have checked the documentation and didn’t find one.

Added later: There is a very VERY fast way how to get rid of this warning. Guess what. If you perform SELECT * instead of listing the fields so you're now using all fields of your work-area, the warning goes away. I don't want to argue about your good intentions, dear SAP, but what is your estimation that people don't go this way? My estimation? Well, people hate annoying warnings more than sub-optimal database access...

 

Please do: clients 001 and 066

On the other hand there are things that are very good and the right way for the sake of the better world to do in the SAP standard. An example from the recent history would be the blog by Frank Bucholz: How to remove unused clients including client 001 and 066.

See my comment below the blog asking when we don’t install these clients at all instead of following instructions how to remove them. Frank is well known for his effort to make the world better, so the fact that I am highlighting his blog is to appreciate an important piece of information for everyone. It is not a criticism in any way. But it is also an appeal on SAP to finish doing the right thing here – don’t install what we don’t need. Taking care of these clients is for me far more important than adding warnings or removing patterns that add more work for the normal mortals.

</rant>

p.s.: If anyone knows how to get rid of these new “features”, please let me know. If anyone can explain why they are there and I am wrong, please also leave a comment.

Using the output of a Standard/Already Developed ALV report

$
0
0

So Recently, in one of my Objects that a member of my team was supposed to develop an ABAP Report which had to have an output that was exactly same as the one of the standard Transactions(IE05) . 

 

Here's what i ended up doing:

 

  1. We need to call the SET method of the class CL_SALV_BS_RUNTIME_INFO as shown below :
      • cl_salv_bs_runtime_info=>set(

                             EXPORTING

                                 display  = abap_false

                                 metadata = abap_false

                                 data     = abap_true

                          ) .

      •   What this does is to tell the system that the report has to be running with no DISPLAY no METADATA on only get the ALV DATA.                   

 

     2. Z* report where i used the used the SUBMIT Statement so call the standard report .

                              SUBMIT riequi20 AND RETURN .

    • The AND RETURN addition ensures that the the control is returned to the calling program .
    • You can use the various other additions of the SUBMIT statement to pass on the selection screen parameters .

         3. Next use the GetDataRef method of the class CL_SALV_BS_RUNTIME_INFO to get the actual data  into a local object  type ref'ed to DATA.

                        DATA obj_data TYPE REF TO data .

                       FIELD-SYMBOL <fs_data> TYPE any .

     

                        TRY.

                             cl_salv_bs_runtime_info=>get_data_ref(

                              IMPORTING

                                  r_data = obj_data

                                 ) .

                              ASSIGN obj_data->* TO <fs_data>.

     

                        CATCH cx_salv_bs_sc_runtime_info.

                             Write : / data not captured.

                         ENDTRY.

          4. The data from the ALV should now be in the fs_data Field-Symbol. We can now change or do any further validations on it.

     

    Thanks to Glen Simpsons Blog for this, it saved me lot of time .

     

    Oputput Problems

     

    I ran into one tiny little problem. Some of the fields like the System Status and the User Status and others did not have any values in them. This was very Weird as when i Ran the Standard report with the same selection screen parameters the values were populated.

     

    So as mentioned in my SCN discussion.

     

    I had the complete set of data that the Standard report outputs in my internal table and i just the ALV Class to output it to my screen.

     

    PROBLEM:

     

    But after checking the data (100+ fields) in the ALV report my Z program creates, I found that some of the Fields which the Standard report does have values for are not populated.

     

    Debugging:

     

    So, i decided to debug the Standard program so see why i didn't get some of the fields into my output.

     

    Turned out that the internal table that was being fed into the REUSE_ALV_GRID_DISPLAY( Location: RIEQUI20 > MIEQUI20 > MIOLXF14 line 108 of include )  FM in the Standard report also does not have the values for the same fields when the FM for ALV is called. (PFA image of the point where the FM is being called.(in case some of you want to debug it yourselves )

     

    e.g. : the value of the table OBJECT_TAB-STTXT is Empty  when the REUSE_ALV_GRID_DISPLAY is called ,

    but in the final ALV output  the fields System Status (STTXT) has the corresponding values . This was very troubling and as with all such situations it had a very simple solution.

     

    http://f.cl.ly/items/0L3X2H2107043c3G242G/Screen%20Shot%202013-08-03%20at%202.20.54%20PM.png

     

    Solution and Reason


    What was happening was that the there was a default LAYOUT on the ALV screen that was set based on which the standard program did not fetch some of the fields that it did not have to display.

    So i went into the standard Report output, click on the change layout button (Ctrl + F8 ) and create a New Default Layout with all the fields i wanted an output for .

     

    Hope this helps some save some time.

     

    Have a Good day.

    Amarpreet.

    Protecting ABAP code against Directory Traversal Attacks

    $
    0
    0

    as pointed out by Horst Keller in one of his blogs, security is important and nothing which should be an afterhtought. Also it is nothing that can be solved by SAP alone but a topic for each and everyone developing code. However SAP has quite some functionality, that assist you to counter attacks more easily.

    In this blog, I will show you, how to protect access to file against directory traversal attacks.

    Directory traversal attacks often also called path traversal attacks try to abuse insufficient sanitization and validation when taking user input as (part of) filenames. One simple example could be the ability to create a file with some input on the application server. A very simple program to do this could look like shown below:

     

    REPORT Z_UPLOAD_NO_CHECK.
    PARAMETERS: pv_fname TYPE c LENGTH 64 LOWER CASE DEFAULT 'test.txt',
                pv_text 
    type string LOWER CASE DEFAULT 'This is the file contents'.
    DATA        lv_full_name TYPE string.

    AT SELECTION-SCREEN
    .
     
    if pv_fname = ''.
       
    MESSAGE 'You need to specify a file name.' TYPE 'E'.
     
    ENDIF.
      lv_full_name
    = '/tmp/' && pv_fname. "Assume enduser inputs only a relative file name.

    START-OF-SELECTION
    .
     
    OPEN DATASET lv_full_name FOR OUTPUT IN TEXT MODE ENCODING UTF-8 .
     
    IF SY-SUBRC <> 0.
         
    MESSAGE 'Could not open file' && lv_full_name TYPE 'E'.
     
    ELSE.
         
    TRANSFER pv_text TO lv_full_name.
         
    CLOSE DATASET lv_full_name.
         
    Write :/ 'contents was saved to:', lv_full_name.
     
    ENDIF.

    What the program does, is simply asking for a file name and the content for the file. It just assumes, that the file name will be relative (like test.txt) and not absolute (like C:\TEMP\test.txt or /tmp/test.txt) and therefore just concatenate the directory the files shall be placed into and the filename itself. In the end it will open the file for writing and put the input of the user into the file.

     

    What’s wrong with this? Two things:

    • The developer defines the directory where to place the files. If the directory does not exist, the admin can only request a new program.
    • The code does not check, whether the file will really end up in /tmp or somewhere else on the server.

    Let’s have a closer look at the second issue. Let’s assume we use a filename like ../(etc/passwd. What happens? The code will concatenate ‘/tmp/’ and ‘../etc/passwd’ into ‘/tmp/../etc/passwd’. In case the operating system is unix based, the system would understand ‘/etc/passwd’ as the file intended to be accessed. It is obvious, that this was not the intention of the programmer. In fact, the attacker would be able to access many operating system files which definitly should be considered a risk using this program.

    So how can we avoid this. For sure, you can just search for ‘../‘ occurring in the input and remove that part. However if you are on a Windows based OS, this is not ‘../’ but ‘..\’ and also what happens if there are more dots, . In addition, the characters may be encoded for instance using UTF-8 and therefore look even different again. So doing this by searching just for some characters, is not that easy. You can have a look at the Wikipedia article on directory traversal attack to get an idea what is involved to protect your code.

    To acounter such risks, the ABAP application server has capabilities to do this for you. As part of the function group SFIL, there is the transaction file, where you can define logical directories and logical filenames and there are function modules like file_get_name and file_validate_name to use the information captured with file to create filenames or verify, whether they are valid. This functionality is Unicode enabled, OS aware and is able to understand the input and interpret it, like an operating system would do.

    Especially when using file_get_name, you may want to check your SP level, as there have been significant improvments done there. Please check SAP note 1497003 for more details.

     

    The SFil Infrastructure

    The function group SFil provides the functionality to easily handle access to files, by providing  logical filenames to developers and functions to map these logical filenames to physical filenames by the administrator.

     

    Logical Filenames

    Logical filenames in ABAP are patterns used to create or verify filenames used on a server or client system. Logical filenames include information about

    ·         The name to refer to the logical filename

    ·         The pattern for the filename

    ·         A data format (ASCII, Binary, Directory, …)

    ·         Application area

    ·         Optional: a logical path

    The filename portion defined can be absolute (start from a root) or relative. If you want to specify filenames outside the working directory of the app server, I suggest you use the logical directories.

    Filename pattern may include predefined patterns like date, time, OS or patterns based on profile parameters and more. You can even have the program provide his own input to the parameters using the parameters <PARAM_1> to <PARAM_3>. However I would recommend not to make use of these parameters for file name validation.

     

    Logical Paths

    Logical paths are an abstraction to the directories on different operating systems. The advantage here is the ability to specify one directory per OS, even though you only have one common logical directory name. For instance SAP delivers a logical path called ‘TMP’, which already contains the directories to be used to place temporary files on unix and windows operating systems.

    log_path.png

    File_Get_Name

    Using the function module file_get_name, you can create filenames based on definitions in transaction file. You have to provide the logical filename and will get back the filename based on the definition in file. Please note, that the filename will only contain the directory if you request this from file_get_name explicitly.

     

    File_Validate_name

    File_validate_name checks whether a given filename is within a directory specified. You have to provide the filename to be checked and a logical filename to check it against. If you want to check, whether a given file is within a defined directory, you have to define a logical filename of type ‘DIR’ (directory). File_validate_name will not accept logical directories directly.

    If the filename given is not absolute (for instance ‘test.txt’), file_validate_name will make it an absolute one by adding the working directory of the application server (profile parameter DIR_HOME). For this reason, you need to put the intended directory in front of relative filenames before calling file_validate_name.

    File_validate_name will also accept all filenames which are at a deeper level of the hierarchy. This implies that the file/tmp/foo /bar would be a valid file when compared to /tmp in the sense of file_validate_name.

     

    Doing it right

    Getting back to the example above, we have to permit the admin to define where the files are placed and validate the filenames or let the system generate them.

    Let’s first have a look at how generating filenames works. There are two options, how the program can be changed:

    • using a predefined filename for the file to be created
    • verifying the filename provided by the user

     

    Using a predefined filename defined by the admin

    In this case you can create a logical filename like the one below in transaction file. This definition will result in filenames that are valid on unix and windows systems and will contain the date and time of the creation of the filename.

    ZMY_outfile.png

    If we would call file_get_name on 2013-08-06 at 17:35:15, the following filename would be created:

    Operating SystemFile Name
    Unix

    /tmp/test-20130801-173515.txt

    WindowsC:\Windows\Temp\ test-20130801-173515.txt

    Please note, the path on windows is dependent on the profile parameter for temporary files. So the path may look different on your system.

    Using file_get_name and the definition as shown above, we can now create a safe version of the program.

     

    REPORT Z_UPLOAD_FILE_GET_NAME.
    PARAMETERS pv_text  type string LOWER CASE DEFAULT 'This is the file contents'.
    DATA       lv_full_name TYPE string.

    AT SELECTION-SCREEN.

    START-OF-SELECTION
    .* get the filename including the path from the configuration
     
    CALL FUNCTION 'FILE_GET_NAME'
       
    EXPORTING
          LOGICAL_FILENAME             
    = 'ZMY_OUTFILE'
          INCLUDING_DIR                
    = 'X'
      
    IMPORTING
          FILE_NAME                    
    = lv_full_name
      
    EXCEPTIONS
         FILE_NOT_FOUND               
    = 1
        
    OTHERS                        = 4.
     
    IF SY-SUBRC <> 0.
         
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
           
    WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
     
    ENDIF.

     
    OPEN DATASET lv_full_name FOR OUTPUT IN TEXT MODE ENCODING UTF-8 .
     
    IF SY-SUBRC <> 0.
         
    MESSAGE 'Could not open file' && lv_full_name TYPE 'E'.
     
    ELSE.
         
    TRANSFER pv_text TO lv_full_name.
         
    CLOSE DATASET lv_full_name.
         
    Write :/ 'contents was saved to:', lv_full_name.
     
    ENDIF.

    Verifying the filename provided by the user

    In the previous example, the admin has complete control over the files created, however from an application point of view, this may not be flexible enough.

    When the user shall be able to specify a file name, you need to make use of file_validate_name. In this case the admin defines a permitted directory using transaction file.

    Please note, that although you need to specify a directory here, you will have to create this under logical filenames. Also you must specify ‘DIR’ as being the data type for this entry.

    The example again will make use of the predefined logical directory TMP.

    ZMY_outdir.png

    There are some traps you need to get around when using file_validate_name.

    File_validate_name works with absolute filenames only. Therefor you have to make relatives filenames absolute first. Also you must specify the INCLUDING_DIR parameter on file_get_name when accessing filenames of type 'DIR', as otherwise the function will dump.

    A safe version for the program could then look like this:

     

    REPORT Z_UPLOAD_FILE_VALIDATE_NAME.

    PARAMETERS: pv_fname       TYPE c LENGTH 64 LOWER CASE DEFAULT 'test.txt',
                pv_text       
    TYPE string LOWER CASE DEFAULT 'This is the file content.'.
    CONSTANTS   lc_log_fname   LIKE FILENAME-FILEINTERN VALUE 'ZMY_OUTDIR'.
    DATA:       lv_full_name   TYPE string,             " filename
               lv_defaultpath TYPE string.
    INITIALIZATION.* get the path, where the files should be placed from the configuration
     
    CALL FUNCTION 'FILE_GET_NAME'
       
    EXPORTING
          LOGICAL_FILENAME           
    = lc_log_fname
          INCLUDING_DIR              
    = 'X'
       
    IMPORTING
          FILE_NAME                  
    = lv_defaultpath
       
    EXCEPTIONS
         
    OTHERS = 1.
     
    IF sy-subrc <> 0.
       
    MESSAGE ID sy-msgid TYPE 'I' NUMBER sy-msgno
       
    WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
     
    ENDIF.

    AT SELECTION-SCREEN.
     
    IF pv_fname = ''.
       
    MESSAGE 'You need to specify a file name.' TYPE 'E'.
     
    ENDIF.
    * check whether the file name is a relative one
     
    IF cl_fs_path=>create( pv_fname )->is_relative( ) = abap_true.
       
    " file name is a relative one, put the default path before the filename
       
    " we also need to check in this case, as '/tmp' + '../etc/test.txt' is still outside /tmp
        lv_full_name
    = lv_defaultpath && pv_fname.
     
    ELSE.
       
    " file name is already absolute, just use it.
        lv_full_name
    = pv_fname.
     
    ENDIF.

     
    CALL FUNCTION 'FILE_VALIDATE_NAME'
       
    EXPORTING
          LOGICAL_FILENAME           
    = lc_log_fname
       
    CHANGING
          PHYSICAL_FILENAME          
    = lv_full_name
       
    EXCEPTIONS
          LOGICAL_FILENAME_NOT_FOUND 
    = 2
          VALIDATION_FAILED          
    = 1
         
    OTHERS                      = 4.
     
    IF SY-SUBRC <> 0.
       
    IF SY-SUBRC > 1.
         
    MESSAGE ID sy-msgid TYPE 'A' NUMBER sy-msgno
           
    WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
       
    ELSE.
         
    MESSAGE 'The filename >' && pv_fname && '< is not valid, please try again.' TYPE 'E'.
       
    ENDIF.
     
    ENDIF.

    START-OF-SELECTION.
     
    OPEN DATASET lv_full_name FOR OUTPUT IN TEXT MODE ENCODING UTF-8 .
     
    IF SY-SUBRC <> 0.
         
    MESSAGE 'Could not open file' && lv_full_name TYPE 'E'.
     
    ELSE.
         
    TRANSFER pv_text TO lv_full_name.
         
    CLOSE DATASET lv_full_name.
         
    Write :/ 'contents was saved to:', lv_full_name.
     
    ENDIF.

     

    If you need more information on the functions used above to protect against directory traversal attacks, you can allways have a look at the secure programming guide, the documentation for transaction FILE, for the function module file_get_name and for the function module file_validate_name.

    Pictures from MIME Repository in HTML

    $
    0
    0

    This is about embedding a picture that is stored in the MIME repository in an HTML-file. I will show two ways of accessing the picture:

     

    • using an API
    • using the ICF

     

    There are many ways of displaying an HTML file. Again I will show two ways:

     

    • using the SAP GUI Control Framework
    • using the ICF

     

    The methods for accessing the MIME repository and displaying the HTML file can be combined.

     

    The code examples shown in the following partly use ABAP 7.40 syntax. To run on 7.31, 7.03, 7.02, 7.0 you have to replace some expressions with the respective statements.

     

     

    Accessing the MIME Repository with an API

     

    In order to get a picture stored as a node of the MIME repository into an ABAP program you simply use code as follows (before 7.40, you have to declare DATA img TYPE xstring before the method call instead ot the inline declaration):

     

    cl_mime_repository_api=>get_api( )->get(
            EXPORTING i_url = `/SAP/PUBLIC/BC/ABAP/mime_demo/ABAP_Docu_Logo.gif`
            IMPORTING e_content = DATA(img) ).

     

    After this, the xstring img contains the GIF-picture passed to the GET-method.

     

     

    Using the Picture in the SAP GUI Control Framework

     

    After loading the picture into ABAP, you can embed it in an HTML-file shown in the browser control of the SAP GUI Control Framework (CFW). To do so,you must load it into the browser control. Unfortunately, method LOAD_DATA of CFW class CL_GUI_HTML_VIEWER was created in Release 4.6 and does not accept strings that were added to ABAP in Release 6.10. Therefore, you have to convert the above xstring to an internal table first:

     

    TYPES: pict_line(1022) TYPE x,

           pict_tab TYPE STANDARD TABLE OF pict_line WITH EMPTY KEY.

     

    DATA(length) = xstrlen( img ).

    WHILE length >= 1022.

      APPEND img(1022) TO pict_tab.

      SHIFT img BY 1022 PLACES LEFT IN BYTE MODE.

        length = xstrlen( img ).

    ENDWHILE.

    IF length > 0.

      APPEND img TO pict_tab.

    ENDIF.

     

    Now you can load it:

     

    DATA pict_url TYPE c LENGTH 255.

    DATA(html_control) = NEW

         cl_gui_html_viewer( parent = custom_container ).

    html_control->load_data(

      EXPORTING

        url          = 'picture_url'

        type         = 'image'

        subtype      = '.gif'

      IMPORTING

        assigned_url = pict_url

      CHANGING

        data_table   = pict_tab ).

     

    The URL returned by that method in pict_url can be used in an HTML-File loaded into the same browser control:

     

    DATA(html_tab) = VALUE html_tab(

      ( '<html><body><basefont face="arial">' )

      ( 'Picture with CL_GUI_HTML_VIEWER<br><br>' )

      ( '<img src="' && pict_url && '">' )

      ( '</body></html>' ) ).

    html_control->load_data(

      IMPORTING

       assigned_url = html_url

      CHANGING

        data_table   = html_tab ).

     

    html_control->show_url(

       EXPORTING

         url = html_url ).

     

    Before Release 7.40 you have to APPEND the lines to html_tab of course.

     

    Since Release 7.02 there is also the convenience class CL_ABAP_BROWSER, that encapsulates CL_GUI_HTML_VIEWER and does the loading for you:

     

    DATA(ext_data) =

      VALUE cl_abap_browser=>load_tab( ( name = 'PICT.GIF'

                                         type = 'image'

                                         dref = REF #( pict_tab ) ) ).

     

    DATA(html_tab) = VALUE cl_abap_browser=>html_table(

          ( '<html><body><basefont face="arial">' )

          ( 'Picture with CL_ABAP_BROWSER<br><br>' )

          ( '<img src="PICT.GIF">' )

          ( '</body></html>' ) ).

     

    cl_abap_browser=>show_html( html        = html_tab

                                         data_table  = ext_data ).

     

     

    You only have to pass a reference to the picture table to method SHOW_HTML that is the assigned the same name as it is used in the HTML-File.

     

    Using the Picture in ICF

     

    In order to display an HTML-file using the Internet Connection Framework (ICF), you can create an HTTP service in transaction ICF and provide the HTML-file in the handler class of that service. The following  method implementation sends data depending on the form field pict_flag of the URL. If pict_flag is not X, an HTML-file is sent, where the address of the picture is the current address concatenated to the form field pict_flag with value X. If pict_flag is X, the picture in our xstring img is sent. With other words, we use the same HTTP-Service to send the HTML as well as the picture embedded in the HTML.

     

    METHOD if_http_extension~handle_request.

      DATA(pict_flag) = server->request->get_form_field( name = `pict_flag` ).

      IF pict_flag <> abap_true.
        DATA(picture) =
          get_address( server = server
                       application = `/sap/bc/abap/demo_mime` )  && `?pict_flag=X"`.
        DATA(html) =
          `<html><body><basefont face="arial">`     &&
          `Picture from API<br><br>`                &&
          `<img src="` && picture&& `">`            &&
          `</body></html>`.
         server->response->set_cdata( data = html ).
      ELSE.
        server->response->set_data( data = img ).
      ENDIF.

    ENDMETHOD.

     

    The helper method get_address looks as follows:

     

    METHOD get_address.

      cl_http_server=>get_location(

         EXPORTING

           server      = server

           application = application

         IMPORTING

           host         = DATA(host)

           port         = DATA(port)

           out_protocol = DATA(protocol) ).

      IF host IS NOT INITIAL AND port IS NOT INITIAL AND protocol IS NOT INITIAL.

        address = protocol && '://' && host && ':' && port && application.

      ENDIF.

    ENDMETHOD.

     

    Accessing the MIME Repository with ICF

     

    As Sandra Rossi pointed it out: If a path in ICF is built in the same way as a path in the MIME repository and if an HTTP-service in ICF is handled by CL_HTTP_EXT_WEBDAV_PUBLIC, you can directly use the URL of that service to address the corresponding MIME objects (with other words, the predefined handler sends the contents of the MIME object as we have done in our own method above).

     

    Accessing ICF from CFW

     

    If you have such an ICF node addressing a picture, you can use it in HTML-files shown in CFW.

     

    DATA icf_node TYPE string.

    CONSTANTS path TYPE string VALUE `/sap/public/bc/abap/mime_demo`.
       
    cl_http_server=>get_location(
      EXPORTING
        application = path
      IMPORTING
        host         = DATA(host)
        port         = DATA(port)
        out_protocol = DATA(protocol) ).
    IF host     IS NOT INITIAL AND
       port     IS NOT INITIAL AND
       protocol IS NOT INITIAL.
      icf_node = protocol && '://' && host && ':' && port && path.
    ENDIF.


    DATA(html) =
      `<html><body><basefont face="arial">`                &&
      `Picture from ICF<br><br>`               &&
      `<img src="` && icf_node && `/ABAP_Docu_Logo.gif">`  &&
      `</body></html>`.

    cl_abap_browser=>show_html( html_string = html ).

     

     

    Accessing ICF from ICF

     

    Of course and last but not least you can embed a picture addressed with ICF in an HTML sent by an HTTP-handler in ICF.

     

    METHOD if_http_extension~handle_request.

      DATA(picture) =
        get_address( server = server
                     application = `/sap/public/bc/abap/mime_demo` )  &&
       `/ABAP_Docu_Logo.gif"`.

        DATA(html) = `<html><body><basefont face="arial">`     &&
                     `Picture from ICF<br><br><br><br>`        &&
                     `<img src="` && picture&& `">`           &&
                     `</body></html>`.

        server->response->set_cdata( data = html ).

    ENDMETHOD.

     

    The helper method get_address is the same as above.

     

     

    I guess, you have to play around with that a bit.

     

    The working examples I copied from above will be delivered with 7.40, SP05 and the output always looks like something as follows:

     

    pict.gif

     

     

     

    PS:

     

    As a third state of cruelty you store also the HTML-file in the MIME repository and access it either by the API or by ICF.

     

     

     

     

    Stop writing Technical Documentation nobody will ever read

    $
    0
    0

    If you're an ABAP developer, answer me: how many times do you really read the technical documentation of a Z program you're about to change? And for the times you did read the documentation, answer these: was it of any help in understanding what the code is doing? Was the documentation up to date with the source code?

     

    In this blog, I'll try to tell you why I think we should get rid of poorly created Technical Documentation (I'll refer to them as "TDs" in this article), and change to a better approach. Many projects I've been in the past can SHIFT+DEL ALL of their TDs, and nobody would ever notice. Here's why.

     

    ------------------------------------------------------------

     

     

    TDs with 10 pages for 500 lines of code

     

    First of all, I hate documentation templates. I know why it was created in the first place, but most times that "old word template" is made by someone who knows nothing about code documentation.

     

    Why do I have to add field name, description, type and length for every single field of a Z table I created inside a word document? Why do I have to specify all selection screen fields? Why do I have to copy things over from the Functional Specification?

     

    Once upon a time I was in a project where we had to create a huge set of Z tables. Me and my ABAP friends had to write down in a word document every single field, description, data type, length, data elements, domains, etc, for every table we created. A huge amount of time lost writing a document that will be out of date as soon as a small field description is changed. And if we missed something, a reviewer would blame us for making poor documentation.

     

    Due to those crazy and stupid templates, we end up having small source codes for simple processes with huge documentations. Documentations nobody ever care to read.

     

     

     

    Coding ABAP in a Word Document

     

    This one is classical. Some ABAP developers just don't care at all about code documentation. They don't write comments in the source code, and whenever they get a change they open a new word document, copy-paste the abap code from se80, name the file "SD.001 - My Program - TD" or something and consider they development is finished.

     

    CTRL+C CTRL+V from ABAP to a Word Document.... JUST DON'T. It would be better if those guys doesn't even started making the "filler" file. The funny fact about it is that some reviewers consider this outrageous thing as something completely "legal".

     

    It's all about the package: The project want to have the TD file; it doesn't matter if it's a piece of garbage. And I've seen a "CMMI level 5" software factory doing that. Way to go buddies...


     

     

    Creating a DUOBM (Document Understandable Only By Me)

     

    Ok, so the guy doesn't have to follow a stupid template, nor intend to make it a carbon copy of se80. But guess what: he also doesn't care about explaining to another developer how his code works.

     

    The whole point about having a software documentation is so other developers can understand the software. If one fail in writing a good documentation he's also failing in delivering useful information about his creation. That development is a great candidate to become an "old and ugly Z monster nobody knows what is doing".

     

    One of my favorite SAP technical documents of all time is the explanation of the good old iXML library. Take a look at this: http://help.sap.com/saphelp_nwpi71/helpdata/de/86/8280c012d511d5991b00508b6b8b11/content.htm . It's fun to read, it's well organized, and the writer knows he's explaining things to another developer. That's a major issue with software documentation in SAP projects: almost nobody cares about who's coming next. Let's just deliver everything until Go Live, make a Happy Hour and move to new projects. G-R-E-A-T.

     

     

    ------------------------------------------------------------

     

    See? These are things me and my ABAP friends faced tons of times in Brazilian and International projects. Many gigabytes of word documents created because the project methodology said they must exist before software can pass through the homologation process. A sad truth.

     

    Hopefully now you're not thinking I'm nuts by saying we should get rid of those documents. But don't get me wrong: I do like the idea of having a document explaining the software. Maybe not in the form of an word file: it can be an document created inside the SAP system, some entry in a CMS or even a small HTML file created with ASCIIDOC . But I strongly think we should be making things differently.

     

    Therefore, I know a couple of things that can help making software documentation a relevant piece of information. Turns out they are very simple things people tend to ignore.

     

    ------------------------------------------------------------

     

     

    What would you like to read in a Technical Documentation?

     

    Get in front of a mirror and ask yourself: What you like to read in a TD? A lot of technical junk you could easily find by going to the relevant transactions, or something that helps you understanding the technical approach of that code?

     

    Why you choose to add parallelism? Why did you have to use that complex recursive logic? A call transaction over a BAPI for VA02... why did you choose to do that? How your objects communicate with each other? Why that small functional process described in the functional specification turned out to be 5000 lines of code?

     

    Answer questions like these on your TDs. Your ABAP colleagues will thank you for that.

     

     

     

    Don't get too much "techy"

     

    This may sound odd at first: if we're doing the Technical Documentation, why can't we go deep down into technical stuff? Because you should explain these things using comments. I've seen a lot of TDs trying to explain small little pieces of coding techniques inside the documentation, but I strongly believe these are things that should have been added as comments in the source code.

     

    Example: "no read binary search on table X - can't sort it with this key because of <something>". That has much more value as a commentary instead of going inside the TD. People forget that comments are also a way of documenting what you're doing. Most times when we're trying to correct a small issue, we go directly to the debug screen, trying to solve the issue. Small and relevant comments can be extremely helpful in those situations.

     

    Oh, and I'm yet to see a developer asking for the TD when his boss starts screaming on him about a bug in an old production code...

     

     

     

    If you can't drop the template... what about a new one?

     

    Technical Documentation templates were probably created by someone that though those were the relevant information they would like to see in the future. But sometimes those people are not experienced developers, so how can they know what really has to be in there?

     

    If you're in a place where you can't stop using a "closed" template, make suggestions to remove the useless stuff and add some guidelines to help others adding value to that document. I bet you managers and leaders will change if it's for the best.

     

    I had the experience of completing changing a project template once, and everybody loved. Even a functional consultant friend of mine was always saying about how he could read my TDs to help him understanding the programs from a technical perspective.  "A functional consultant reading a TD"? Believe, it's true!

     

     

     

    Starting reading some TDs!

     

    I didn't realize these tips out of nowhere. When I was a very young ABAP n00b, I believed in unicorns, fairies, dragons and also in TDs. Yes, everytime I had a problem I tried to read the TD first, to see if it could give me a clue about the code. However, most times they failed miserable.

     

    As time passed by I started debugging the code without reading anything at all. People wasted time doing TDs, but I didn't want to waste my time reading them.

     

    It happens that old habits die hard, and even today I do like to take a sneak peak inside TDs. And yes, they've been the same in ABAP projects since 2006. I found cool documentations throughout these years, and they were the ones that opened up my eyes and made me change the way I wrote documentations.

     

    Start reading some, and change too.

     

     

    ------------------------------------------------------------

     

     

    I think that's everything I have to say about code documentation for now. I wrote about them in the past, but I thought this was a good time to raise the topic once more (this blog motivated me).

     

    Please, share your experiences with ugly/cool documentations on the comments section. Let's debate and change this old "non-documentation" process.

     

    Thanks very much for reading!

     

    Best Regards,

    Mauricio Cruz.


    Where Used List for Adobe Form Objects

    $
    0
    0


    There is no standard  Where-Used List option for Adobe form & interface available in transaction SFP. So I have developed one program for ‘Where-Used list for Adobe Form objects’. This program will help you finding where used objects related to adobe form & interfaces.

               transaction SFP.jpg

    Below are brief specifications about this program:

    • Gives where used list for selected object used in:
      • Interface
      • Output types
      • Program & routines
      • PP related config with program name & routine.
    • For Interface, gives where used adobe form list
    • Program also does global search for hard-coded adobe form text

     

    Steps To use this Program

     

    • Download below program and upload it on your system. Upload text element list also.
    • Run this program. Below selection screen will appear.

              input screen.jpg

    • Input selection :
      • Select Adobe form, if you want to search where used list for Adobe form. Give Adobe form name as input. You can use F4 search help.
      • Select Interface, if you want to search where used list for Interface. Give interface name as input. You can use F4 search help.
      • If you tick Global search check box, program will do global search for given name as a hard coded text in all ‘Z*’programs.  It will then show list of objects where it find hard code text.

     

    Output

     

         Sample input :

    input screen1.jpg

         Output for above input:

    output.jpg

     

        

    ABAP Objects - I Want to Believe - Part 02

    $
    0
    0

    ABAP Objects is the Way Forward – I Want to Believe – Part Two

     

    http://scn.sap.com/community/abap/blog/2013/07/04/abap-objects--i-want-to-believe

     

    The above blog – first part of this series - can be summarised as follows:-

     

    ·         I want to prove that writing new programs in OO really is better than the “old” way

     

    ·         I have a real life project which allows me to do just that

     

    ·         I am going to try to document how I go on this step by step

     

    ·         The example will be silly, but based on the real project

     

    ·         I am using test driven development, so I am starting with the tests

     

    I got as far as writing one test, which does not even compile yet. The next steps are as follows:-

     

    ·         Try and create my unit tests properly

     

    ·         See if I can convince my colleagues that unit testing is a Good Thing

     

    ·         Develop the program with regular user feedback

     

    ·         See if anything I develop in this project can be re-used in subsequent projects

     

     

     

    I apologise I advance, this is far too long, as usual, and I go off topic all the time, also as usual.

     

     

     

    Why am I doing this in the first place?

     

     

     

    I am now stuffed full of new concepts from reading assorted OO programming books, and am only half way through Robert Martins “Clean Code”, so a nice big new programming project gives me ample chance to try and see if any of this can be applied in real SAP life.

     

     

     

    If I write blogs all through writing this monster application, then I am forced to explain what I am doing, and often if you try to explain what you are doing, it makes you think about what you are doing, and that in turn sometimes leads you to think “WHY am I doing that? I must be a madman”, and then a better way may suggest itself.

     

     

     

    If I publish my reasoning on the SCN then chances are a better programmer than me will read this and tell me where I am going wrong and point me in the right direction. Conversely if a newcomer reads this then they might get some helpful hints on how to things, and as I said even if I am wrong then the comments should hopefully correct the myriad mistakes I make.

     

     

     

    As anyone who has read any of my prior blogs will know, I am also going to continually wander off the subject and then slap myself round the face with a fish to get back on topic.

     

     

     

    So, off we go, let’s talk about UNIT TESTS.

     

     

     

    UNIT Tests

     

    2.01 Unit Tests.png                      

     

    Figure 2.1 Unit Tests

     

     

     

    It is time to talk about the scope of the unit tests. When I published the last blog, lots of people said you should not test private methods. If you look on the internet you can see a lot of debate about this with people very passionate about this, some say yes, some say no, though I get the feeling the consensus is “no”.

     

     

     

    The argument seems to be that if you test the public method, then if one of the underlying private methods breaks then that failure will propagate itself to the public method, so your test of the public method will then fail.

     

     

     

    That way you still know instantly if your change has broken anything, and then you can change the implementation of the public method (i.e. refactor it) as much as you want without having to change lots of low level tests. That logic sounds good I have to agree.

     

     

     

    I am not trying to give away too much about what I am doing in real life, but it is going to become obvious it is all to do with the SAP Variant Configuration module. In my example I am building a monster for Baron Frankenstein based on various desired monster attributes, in real life my application is going to be a bit of a monster also.

     

     

     

    I have one massive unit test, what I call a user acceptance test. This takes the desired characteristics and the output is a bill of materials from which to build the monster.

     

     

     

    The basic structure is one public method, which under the hood will call about a dozen private methods, each of which represents a square on a VISIO flowchart. Each “square” in turn will call about ten other private methods.

     

     

     

    A Finger of Fudge is Just Enough

     

     

     

    From reading the assorted literature about test driven development it seems there are three rules, and you are supposed to go around in a really small circle, a circle Robert Martin says should take only a few minutes.

     

     

     

    ·         No code can be written until you have a failing Unit Test.

     

    ·         Only write enough of the Unit Test to fail.

     

    ·         Write just enough code to make the Unit Test pass.

     

     

     

    OK, so I have a failing unit test, my very high level monster making user acceptance test. I know the inputs and expected outputs.

     

     

     

    I need to actually define the implementation of the class under test and the main method which is being tested. I can create an empty implementation of this main method, and then in my test class I have a test method which sets up the inputs (GIVEN) calls the main method of the class under test with the correct signature (WHEN) and then does some assertions to make sure the output is correct (THEN).

     

     

     

    Everything now compiles, I can get as far as doing the TEST -> UNIT_TEST from the ABAP editor, and get a screen showing me a red light because all my assertions fail. I have now finished the section part of the TDD cycle, I have written JUST ENOUGH for the test to fail.

     

     

     

    I now need to write just enough production code to make the test pass. Hang on, that is ALL OF IT, all one hundred odd private methods. Somehow I don't think I am going to be able to do this all in one go, and not in under a minute.

     

     

     

    The only way I am going to be able to do this in a way that attempts to follow those TDD rules is to start with tests for the smaller methods – which are all private – and work my way up.

     

     

     

    As I say repeatedly I have a big VISIO process diagram, and for each square I have an equivalent part of a spreadsheet, and I know what values go in and what values are supposed to come out. I will start doing a unit test for each square, and after creating each test I will write the production code. “That’s the way you do it!” as Mr.Punch would say. He was a big fan of test driven development, when he wasn't stealing sausages.

     

     

     

    ABAP Unit – Sharing is Caring

     

     

     

    The other day in my company we had our annual programmers get together where we take a day away from sorting out the users problems and focus on technical things. Ten years ago when we started this we mainly focused on what SAP would describe as “customer program optimisation” where you look at all your Z programs for performance problems.

     

     

     

    As the years go by our relentless focus on this matter means this is no longer a problem in the slightest, so now I have moved the focus to looking at the myriad new technologies available to us with the advent of ECC 6.0 and similar.

     

     

     

    Since I had done quite a few programs with unit tests in them by this stage I thought I would explain to my colleagues what this was all about, as I knew for a fact I was the only one using this tool (ABAP Unit).

     

     

     

    I had been so impressed by the – to me – obvious benefits of an automated testing framework that I thought this would sell itself. However this was the reaction:-

     

    2.02 Unit Test Reaction.png

     

    Figure 2.2. Unit Test Reaction

     

     

     

    I had, in my audience:-

     

    ·         My boss

     

    ·         A BASIS guy, coming up to retirement age, who nonetheless I not happy unless we have the absolute latest version of every single SAP product

     

    ·         A programmer who always uses OO programming for everything and is much further down the path than I am

     

    ·         A programmer who started off using punch cards, and would rather set himself on fire than use object orientated programming, and has called for it to be banned

     

    ·         A programmer who is desperate to learn about all new SAP things, who got confused when I talked about the difference between a static and instance method, and the gung-ho OO programmer then explained that difference

     

    ·         A programmer who might have walked out of the description of ABAP programmers in Graham Robinsons blog “a call to arms”

     

     

     

    To summarise, quite a mixed bag, all of whom had at least ten years SAP experience. None had heard of ABAP unit.

     

     

     

    I like to think I can explain something well when I put my mind to it, and I showed various programs I had written with unit tests, and wrote some lovely pretty pictures on the whiteboard like the consultants do. My boss said I had explained it well, so I thought this was going swimmingly.

     

     

     

    Then the questions started. No-one expects the Spanish Inquisition!

     

     

     

    So…..

     

    ·         No, this test driven development / unit test business wasn’t something I had invented myself

     

    ·         No, this wasn’t something I had got off the internet like ABAP2XLS

     

    ·         Yes, it’s actually a part of standard SAP, a part of SE80

     

    ·         Yes, it does mean you write twice as much code, takes twice as long to write something

     

     

     

    I thought I could get around that last one, by doing another consultant trick and drawing a pie chart. Consultants can convince anybody of anything my drawing a pie hart or dividing a white board into four and writing the option they want into the top right hand square.

     

    2.03 Consultant Diagram 1.png

     

    Figure 2.3 Consultant Diagram 1

     

     

     

    I have seen that technique used millions of times, it usually works, it didn’t work for me - so I tried a pie chart. This time I actually had a bit of logic on my side.

     

    2.04 Consultant Digram 2.04.png

     

    Figure 2.4 Consultant Diagram 2

     

    The point I tried to make is that since 95% of the effort expended on a program is on maintenance, then doubling the time spent on development does not matter that much in the grand scheme of things, if it makes the other 95% of the life cycle faster and more stable.

     

     

     

    I am so lucky where I work. My boss has an open mind. Initially the idea that most of the effort on a program was maintenance came like a bolt out of the blue to him, but when he asked the other programmers if this could possibly be true, and they all nodded, he got it straight away.

     

     

     

    However he raised the obvious point that how can you convince a business manager that something they want right now will take twice as long to delivery initially, because in the long run they will be better off. Most human beings do not care at all about the long run, just what they can get right now, right this second.

     

     

     

    There was a cartoon where Charlie Brown had to go away for a few days and he left Snoopy with three bowls of dog food, and told him not to eat them all at once, but one per day, otherwise he would go hungry. Snoopy ate the first one, and then got more and more agitated and the dive on and ate the other two, and then led back and said “I’m glad I did that – what if tomorrow never came?”

     

     

     

    So, I don’t think management are going to go for this in a big way, and I am fairly certain none of my colleagues are going to start using unit tests. Worse, in nine months time, when they change one or my programs that has loads of unit tests, they won't run them after the change to see if anything has broken.

     

     

     

    However, my boss asked me “have you any proof that this works?”.I replied that I had stuffed a procedural program full of unit tests, and once the program was in production the requesting manager had never once complained, and for the last ten years he had ALWAYS complained that what we wrote was never good enough and always came back half a billion times with corrections to be made.

     

     

     

    Maybe if I make a go of this, it might encourage others….

     

     

     

    Waterfall Am I

     

     

     

    Talking about end users, I have a highly nervous user base, some of whom have grave doubts whether this project (which I am working on and is the subject of this blog, not that you can tell this as I keep diving off down rabbit holes) can be achieved at all. The traditional method of dealing with an enormously complicated SAP project like this this was to take the original user requirements, go and lock yourself in a room for two years to build something, and then give the finished product to the user who wrote the original requirements and be really shocked when they say “this is not what I wanted at all” and then you have to tell them "sorry this is what you’re getting, the projects run out of money, we’re doing a new one next week”.

     

     

     

    This is the “waterfall” method and it is how most of the major SAP implementations I have observed have worked. A few years either side of the year 2000 a lot of consulting companies made all the money in the world using this method. The secret to their success was they could hand over the finished product and then run out the door.

     

    2.05 Waterfall Projects.png

     

    Figure 2.5 Waterfall Projects

     

     

     

    If you look on the internet you would think that this is a thing of the past – nowadays everyone uses extreme programming / agile / scrum” type methodologies in the same way all ABAP programmers use OO programming. Now, is that true?

     

     

     

    Just two weeks ago a colleague of mine from America asked me what in the world “scrum” meant in an IT context, and I gave a simplified explanation which revolved around showing a working prototype to the users every two weeks which just the functions you have built thus far, as opposed to showing them everything after two years.

     

     

     

    He said that sounded great but it “probably wouldn’t work on a massive SAP project”. “Plus Ca Change” as the French say. I wonder how many “waterfall” projects are being done on SAP systems worldwide at this very instant. Probably enough to make the authors of the “agile manifesto” wonder why they bothered.

     

     

     

    The End User is Nigh

     

     

     

    I was dancing around the room with joy today when in the weekly ABAP meeting one of my colleagues said that Michael Bubble was really happy with my colleague’s new SAP custom development. I thought that was great that he could take time off singing to test our programs, but it turns out this was a different Michael Bubble, this one was an end user.

     

     

     

    It turns out the decision had been made to go live with this development but only let one or two users have access to it, and then they would come back with a non-stop stream of bug fixes and enhancements until the development was fit for a human to use. Then everyone else could have it. You might think one or two users is not enough of a broad base to be representative, but it is a hell of a lot better than zero users looking at it before it went live for the whole company, as appears to be generally the case according to anecdotal evidence from all over the world.

     

     

     

    The method above is still a waterfall pig with some agile lipstick on, but how much better would it be to be able to show the end users something every two weeks, or more likely to be able to sow the constant stream of visitors from assorted areas of the business what we have so far, as and when they arrive. I am going to be allowed to do this, it helps that I know a load of the end users, having been in the company for a long time.

     

     

     

    As I am going to break down the development into bite size chunks, one VISIO square at a time, it is likely that each week I will have one or two squares each week finished. However a unit test is a bit of an esoteric thing to show somebody, especially given that if the test passes you see nothing at all except a blue line at the bottom of the screen. Strangely enough, that does not blow the socks off the end users and make them say “my god I am impressed, all my worries have melted away”.

     

     

     

    Simulate, late, breakfast show

     

     

     

    My idea was to have an executable program that showed the sample input data on a selection-screen, and then call all squares thus far written one after another and use the application log to tell the user in exquisite detail what is going on, so they can see the logic for themselves and if they don’t like it, start arguing with the guy who wrote the specification, literally over a year before it is likely to reach its final state and be foisted upon them.

     

     

     

    Here is a great SDN article about using the application log to display complicated information:-

     

     

     

    http://wiki.sdn.sap.com/wiki/display/profile/2007/07/09/Message+Handling+-+Finding+the+Needle+in+the+Haystack

     

     

     

    I could not find a practical application for this until now, this is a perfect fit for what I want.

     

     

     

    Break Down and Cry

     

     

     

    I am going to break down my monster making process as follows:-

     

    ·         Each square is a “main process” – it will have it’s own unit test

     

    ·         That square is usually a series of sub-processes. If that process is not utterly basic (like adding two values together) than that gets its own method

     

    ·         Each sub process is a series of process steps, in which you either get some data from the database, or do some sort of calculation on that data. Since I am just working towards building a BOM based on a set of characteristics those are the only two things I have to worry about.

     

     

     

    My simulator program will just call all the squares thus far written one after the other and then display an application log in a nice tree format, so a user can drill into the bits they are interested in.

     

     

     

    How you break up methods seems to be the subject of a fierce debate in the OO world, with people burning each other at the stake for heresy if they have an opposing view on this subject. What is this argument?

     

     

     

    You’ve got that ONE THING

     

     

     

    Robert C.Martin says that a method should do one thing only and do it well. In procedural programming it is not uncommon to see a FORM routine ask the user something, then go off to the database and fill up an internal table, and then loop over that table doing assorted things to the data. If you are REALLY lucky the call to the ALV (or WRITE statements if you are where I used to work) is in another FORM routine.

     

     

     

    How often have you seen this:-

     

     

     

    START-OF-SELECTION.

     

     

     

    PERFORM GET_AND_TRANSFORM_DATA.

     

     

     

    END-OF-SELECTION.

     

     

     

    PERFORM DISPLAY_DATA.

     

     

     

    It’s a good job that virtually nobody understood what END-OF-SELECTION really meant, otherwise instead of two FORMS there would be just the one (in case you are wondering END-OF-SELECTION is only meaningful if you are using a logical database in your program).

     

     

     

    I’ve noted before that many people have not even made the leap to procedural programming yet; they still write everything in one big list after START-OF-SELECTION. That is the opposite of what Robert Martin is looking for.

     

     

     

    He once wrote a blog called “extract till you drop” where he took a Java program and broke it down into smaller and smaller chunks. That got a load of arguments from both sides as to whether this is a good thing or not.

     

     

     

    The ABAP programming guidelines book from SAP Press says “modularise don’t atomise: and when the following blog was published:-

     

     

     

    http://scn.sap.com/community/abap/blog/2013/07/09/why-should-we-write-clean-code

     

     

     

    you will note the very first comment says that “light weight” classes i.e. classes or methods that don’t do very much, are more suited for Java than for ABAP.

     

     

     

    Well I don’t know the answer, I am still a beginner, I am going to split things up in a way that seems right. Everything is going to be a local class / method inside one Z program to start off with, hopefully the more I get into this the more obvious the correct way to split things will become.

     

     

     

    It has been said that is one of the benefits of test driven development – it forces you into having the correct structure. We shall see….

     

     

     

    Last of the Cohesions

     

     

     

    I find that often when I read the reams of material available about how to write object orientated programming you get seemingly contradictory instructions. Whilst that is frustrating on the face of it, the good thing is it forces you to think about both ways to do something and make your own mind up. It is better to have two options than to have no clue at all as to what to do.

     

     

     

    As an example, I mentioned earlier the idea of “high cohesion” which says that if a variable gets used in all the methods of a class then you should make it a “member” variable (sort of like a global variable but only within one instance of a class) as opposed to constantly passing it backwards and forward in the signatures of all the methods. This would make the signature simpler.

     

     

     

    My gut feeling however was to stick with the big signature as it made it obvious exactly what a given method needed to do its work, and what values it defined or changed.

     

     

     

    There is a saying in some places in the world that if you don’t like the weather just wait a few hours until it changes. It seems to be like that with the OO books I read, if I decide to do something then sooner or later I come across an argument which justifies my opinion.

     

    In this case we have the idea of “temporal coupling”. This says that it is a Bad Thing if you have two methods, and if you change the order in which they are called then the program behaviour changes unexpectedly in a way that is difficult to debug.

     

     

     

    I am making no secret that the area I am working in at the moment is variant configuration, and I have a series of methods which correspond to VISIO squares and the point is that it is highly likely the flow chart will change and then the squares will get called in a different order. They say that a lot of the art of programming I making a guess as to what my change in the future so you can prepare for it, and to my mind the order of the squares in the flowchart changing is a virtual certainty. In a previous project I am aware of a similar flow diagram changed twenty seven times before everyone was happy with it.

     

     

     

    In conclusion, I think I am going to stick with having big signatures in my methods until such time as I find a reason to reverse my position even stronger than the argument that made me choose that position. This might well happen, that is what is so good about learning new things, you can’t be like the sheep described by the Housemartins– “they never questioned anything, they never disagreed, sometimes I think they must have wool in their ears”.

     

     

     

    In unit tests however, I have almost everything as member variables, so I can make the code look like English sentences with virtually no signatures at all. Those methods always run in the same order GIVEN – WHEN – THEN so there is much less danger of “temporal coupling” errors.

     

     

     

    The Revenge of Domain Specific Languages

     

    2.06 Domain Specific.png

     

    Figure 2.6 Domain Specific Language

     

     

     

    Here’s a little blog I wrote, you might want to sing it note for note:-

     

     

     

    http://scn.sap.com/community/abap/blog/2013/01/08/domain-specific-language-in-abap

     

     

     

    The idea of a “Domain Specific Language” is that a business user could look at your code and understand what was going on at first glance, because everything was couched in business terms.

     

     

     

    I was going all out to try and achieve this, and had extracted methods for each line in the spreadsheet so that the sub process methods consisted of lines like DO_SUCH_AND_SUCH( ).

     

     

     

    I copied the names from his spreadsheet. Then when he asked to look at the code I thought he was going to be pleasantly surprised but he looked and said “I don’t understand a word of this”.

     

     

     

    That is because I had still kept some ABAP type words in the code e.g. INITIAL which no-one would understand, and also because I had used the same abbreviations as he had in his spreadsheet. I should really have expanded them back out again to their full English words, we have 30 characters in ABAP - I like to use as many as I can.

     

     

     

    So, I went back and tried again. It can be done – here is a great blog on the use of macros…

     

     

     

    http://scn.sap.com/community/abap/blog/2012/10/28/use-macros-use-them-wisely

     

     

     

    and with a mixture of them and small methods named like English phrases, and then you can have code that a business user can read.

     

     

     

    You might think that is a pointless goal, but I have seen my fair share of business analysts debugging, and one (in Germany) even told me “I can follow whatever you write as long as it is not that object orientated ****”.

     

     

     

    Just as an acid test, if the business user who wrote the specification could read your code, and understand it, and say “yes, that it just what I meant” that would be a good thing, would it not?

     

     

     

    I am sure some people may say “I don’t want users being able to understand my code, if they understand it, then I am out of a job, that is why it is called CODE so no-one can understand it!”.

     

     

     

    Hoots Mon! There is Re-Use, Loose, about this Hoose!

     

    2.07 Hoots Mon.png

     

    Figure 2.7 – da dadadada, da dadadadadaetc

     

     

     

    I keep plugging the SAP Press book by Igor Barbaric called “Design Patterns in Object Orientated ABAP” because that is what got me going into this area in the first place. He said that the first time he did a major application in an OO fashion he ended up with a whole bunch of components that could be re-used in subsequent projects.

     

     

     

    A cynic would say you could do that with function modules, but I am trying to keep an open mind. Also, this is rather like the Moon Walking Bear – you don’t look for it unless you know it could be there. I have to say that even after a short while I am finding things that could be potentially split off into their own Z classes and then re-used elsewhere.

     

     

     

    Since OO design, and unit testing in particular, forces you into the “separation of concerns” where different things are in their own classes, it soon becomes apparent that some classes are clearly specific to the application at hand whereas others are far more generic.

     

     

     

    I never knew there was so much in it

     

     

     

    Naturally if I can find an existing artifact to do want I want I can re-use it. However, one of the “problems” of SAP is that if you have a programming problem to solve, it is almost certain someone has been there and done that and got the t-shirt, and that there is a standard class / method / function module that already does what you want.

     

     

     

    The problem is finding it.

     

     

     

    There are more function modules in the standard SAP system than there are stars in the sky, and more recently a lot of classes / methods as well, though it will take time for them to catch up numerically due to the vast amount of time when OO things were not available in ABAP.

     

     

     

    It is my horrible suspicion that hundreds if not thousands of ABAP programmers spend their day writing functions that are exact copies of things that already exists in the standard system, or creating user exits which achieve functionality which either exists in enhancement pack business functions which have not been activated in their system, or even for things that can be achieved by customising.

     

     

     

    Even worse I think the same thing happens within SAP itself. When I have searched for standard function modules to achieve XYZ I usually get no results or five or six functions doing exactly the same thing.

     

     

     

    When I was reading the book “Thinking in Java”  http://en.wikipedia.org/wiki/Bruce_Eckelsaid the if you did not know the name of a method in the Java GUI “swing” framework you could guess what it was called because the naming conventions were so strongly followed. That does not seem to be case in SAP, if anything there is an utter lack of naming conventions in standard SAP repository objects.

     

     

     

    Even the BAPI naming is all over the place, one of the four principals for BAPIS was supposed to be consistent naming, along with meaningful documentation, he hehe.

     

     

     

    You end up with names containing assorted naming conventions for creating a new business object of some sort.

     

     

     

    With other functions, even if the function at hand is the sort of thing that is re-usable in many contexts, the SAP programmer names it after the project they are currently working on, so you get class names like JAPANESE_MICE_RACING_MATHS_FUNCTIONS which contain many useful mathematical functions which could be used in many applications which are unrelated to mice, or racing, or Japan.

     

     

     

    I don’t like to call a function module with a name like that, as it would confuse the hell out of any casual reader of the code.

     

     

     

    In the end very often we create our own function module or class, and then later on we might accidentally stumble on an identical one in standard SAP.

     

     

     

    Going back to my example, in the spreadsheet was the EXCEL function MAX. I thought there must be one in standard SAP that does the same thing – as indeed there is – but I couldn’t find it for love nor money. A load of Google searching did not help, I searched for function modules and found BELGIAN_TRUFFLE_GROWERS_MAX_VALUE which was almost OK but had a few odd mandatory import parameters.

     

     

     

    In the end I wrote my own method to do this, and then every so often I did another Google search using different parameters – the term “BUILT IN FUNCTION” did not help much for example – and in the end I discovered the standard function is NMAX.

     

     

     

    You are probably thinking “oh my god, what a whinger and complainer he is, always talking about how hard things are to do in SAP”.

     

    Well, I’ll tell you, I never wanted to be an ABAP programmer in the first place!

     

    I...  I wanted to be... A LUMBERJACK!

     

    (piano vamp)

     

    Leaping from tree to tree!  As they float down the mighty rivers of British Columbia!

    With my best girl by my side!
    The Larch!
    The Pine! 
    The Giant Redwood tree! 
    The Sequoia! 
    The Little Whopping Rule Tree! 
    We'd Sing!  Sing!  Sing! 

     

     

     

    I’m a Lumberjack and I’m OK

     

     

     

    As mentioned earlier, when it comes to logging I want to make re-use of CL_RECA_MESSAGE_LIST to handle showing error messages to the user and the application log in general. Great as this is, I am never happy, so naturally I sub classed this and added assorted things of my own.

     

     

     

    For example, sometimes I just want to add a free text message and do not want to have to clutter up the code passing in the message number and type and what have you. This is like the ABAP statement MESSAGE ‘something’ TYPE ‘I’. So I redefine the ADD method and let it accept just a text string and the message type and then pass in suitable generic values for the message class and number.

     

     

     

    This is just an example, and probably not a very good one, the point is every time I feel the lack of some sort of function to do with logging I will add it to be Z message handling class, and this is bound to be re-usable in future applications. OO people keep on and on about how you will end up with a library of re-usable components you can share with your colleagues – or the SCN in general – and maybe they are right. That is after all the purpose of the code exchange, with ABAP2XLS being the best example.

     

     

     

    Local Hero

     

     

     

    That logging functionality will clearly be a Z class as I can be virtually certain myself or someone else will make use of it in the future in another application. However generally, most of the code in a given application will be specific to the application itself.

     

     

     

    So the question is – and this will be the subject of the next blog – do you do your prototype mainly as a Z class, or as a local class, and what are the drawbacks and advantages of both approaches?

     

     

     

    Cheersy Cheers

     

     

     

    Paul

     

     

     

    2.08 Until Next Time.png

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    Trigger ALV data_change event manual

    $
    0
    0

    As we know, when we changed something in the alv grid, event data_changed will be triggered, and we can do some custom check.

    but do you face this situation, after you changed this alv and push on some user command, like 'SAVE', the data_changed event will not be triggered again, because no cell is changed in alv grid, but actually, you want to trigger it and do something you want.

    below i will give you 1 solution to trigger it by compulsion.

     

    in PAI, use method IF_CACHED_PROP~SET_PROP of class CL_GUI_ALV_GRID to set property 'GridModified' as modified, then call CHECK_CHANGED_DATA of CL_GUI_ALV_GRID, data_changed event will be triggered.

     

    1.add code in PAI

    图像 001.png

    2.Run your program and update data, then save.

    图像 007.png

     

    图像 010.png

    3.Push 'SAVE', event will be triggered.

    图像 003.png

    图像 004.png

     

    This is my first time to write blog, please give your comment if you also have some experience on this topic. Thanks.

    Pricing Routines Coding in RV64A*

    $
    0
    0

    Well Friends,

     

    I have written pricing routines quite some times and have been able to deliver things which were simpler of-course with quite ease.

     

    In my current assignment I faced an issue which was quite similar to my knowledge but I was unable to re-collect the solution or even the root cause for the same.

     

    Problem :

     

    The pricing routine written to calculate the rebate value with respect to some quantity base calculation with lot of if and but's not giving appropriate output.

     

    Analysis :

     

    Well, to my surprise when I do it outside the routine passing the same parameters it does not seems to work.

     

    Closer analysis dictates SAP has not yet made the "Fixed Point Arithmetic" flag for the main program and is still using a very old methodology to convert values in appropriate decimals.

     

    If outside the program I set the flag in the attributes of the program and run it, it works just as I want it to.

     

    Key Issue :

     

    Don't want to take the key "Fixed Point Arithmetic" flag for the main program

     

    Reasons:

     

    1. Don't want to modify the main program

    2. In doing so may be... just may be I could affect a lot of things which could result in a bigger disaster.

     

    Solution and Learnings :

     

    Well, I have learned if I want to d write routines in my future projects:

     

    I will create a new Function Group / Class and "Fixed Point Arithmetic" flag for the main program

     

    Create Function modules / methods with passing values as parameters and returning parameters with the value needed.

     

    Embed all calculations inside this FM to have appropriate calculations.

    HOW I DO IT: Purpose and the Creation of a "Deletion Transport"

    $
    0
    0

    Problem Description

     

    When we have a Package which contains a "lot" of sub objects and we want to delete entire Package or bunch of objects under the package.

     

    The problems we might face are:

     

          A) Manual Deletion of objects requires lot of time and we need to delete all the sub objects, release the transport and then delete the Package .

     

         B) Deletion of package in a non development system becomes a problem as we need to delete them in the development system manually

         and  then move the transport across the landscape. And if we want to delete only in a Q system requires complex strategy.

     

    So these problems can be simplified if we have a Deletion transport which would just delete the objects where we want.

     

    Definition of a Deletion Transport:

     

    Basically the definition of a deletion transport in our context is a transport in which all the objects are listed and marked for deletion & When we import this transport into any system the listed objects are deleted.

     

    Situations:

     

    When we talk about situations , it could be of a need when we are implementing a short term 3rd party solutions ( e.g smartShift's smartUpgrade tool) , System Maintenance or a POC project etc.

     

    Lets say

     

    1) You have done a POC in a system and at the end you want to delete all those objects ( say from an IDES system ) .

     

    2) You want to delete the objects in a quality system for some reason, but you dont want these objects to be deleted in the development system .

     

    3) You are doing a maintenance of the development system and want to clean up the list of test objects.

     

    Solution :

     

    There is a field OBJFUNC in E071 table ( Object list of a Transport ). When OBJFUNC is 'D' the activity of the transport on this object would be "Deletion".

     

    But mere setting of this flag in the transport data wont convert a transport into a deletion transport.  The D indicator works only when the objects are not present in the system ( See below screen shot and short text of the field ) .

     

    e071-D.png

     

    So we need a dummy system where our objects are not present and we create a transport in that system with Deletion indicator . This is explained in detail in further section.

     

    What you need :

     

    1) First thing you need is "The problem" itself, Only when you really have this problem then this post makes some sense to you.

     

    2) A Dummy system where none of these to-be-deleted-objects  exist. ( Say IDES or any Other development system than your main development system )

     

    3) Some basis authorization who can help you in Create / Release / Download the K , R files ( In case you dont have the authorization ) .

     

    Steps :

     

    A) From the main system,( Where the objects actually exist) Download the object header data from TADIR into a tab delimited text file   , The first 3 fields are the only required data ( i.e The fields PGMID , OBJECT , OBJ_NAME of the TADIR table ) .

     

    Excel-Data.png

     

    We use this data to create the  TADIR entries in the dummy system. A TADIR entry is mandatory to add the object to a transport.

     

    Caution: Review your object list once it is downloaded. Make sure only those objects which you wanted to delete are present

     

    B) Create the below report program in the Dummy System where we Generate the transport.

     

    Program specification is as below:

     

    B1)  Create a Selection screen parameter P_FILE to read the object list file.

           Create a Selection screen parameter P_TRKORR , for the "Deletion Transport"

           Create a Selection screen parameter P_DEVC for entering a Z dummy devclass

     

     

    Selection Screen.png

    B2) Read the file into an internal table IT_TADIR

     

    B3) Process the IT_TADIR to 1) Create a Dummy entry into TADIR 2) Create an entry into the transport

     

    B4) Sample Code snippet

     

         DATA: W_TABIX  TYPE SY-TABIX.

         CLEAR W_TABIX .

     

         LOOP AT T_TADIR INTO W_TADIR.

     

                W_TABIX = W_TABIX + 1.

     

         *Step1 Create a Dummy Entry into TADIR ,

                W_TADIR-DEVCLASS = P_DEVC.   " Any dummy development class other than $TMP

                MODIFY TADIR FROM W_TADIR.

     

         *Step2 Create an entry into the transport object list , E071

     

                W_E071-OBJFUNC = 'D'.                 " Indicates Deletion

                W_E071-TRKORR = P_TRKORR.     " The input deletion transport

                W_E071-AS4POS = W_TABIX .        "Sequence of the object within the transport

     

                MOVE-CORRESPONDING W_TADIR TO W_E071. " Header data of the object

                MODIFY E071 FROM W_E071.         " Update the standard table E071 for transport

     

           ENDLOOP.

     

    C) Once the program is ready, then Create a Workbench transport with a Target System ( Can be a Virtual System, just to get the transport files ) and a Dummy Development Class.

     

    Execute the program in the dummy system with mandatory input as object list file , deletion transport , and the dummy devclass.

    Selection Screen.png

     

    After the execution is complete, The transport will be loaded with the objects with Deletion Indiactor and the object list looks like below :

    Deletion TR entries in SE01.png

     

    D) Release the transport and Download the K (\usr\sap\trans\cofiles\ ) and R (\usr\sap\trans\data\ ) files of the transport.

     

     

    E) Your "Terminator" transport is Ready!!!  Import the deletion transport into the required system where you wanted those objects to be deleted.

     

    Sample Diagram of the Steps and Process

     

    process.png

     

    Note:

     

    The development class you use in the Dummy system doesn't have any affect . And it need not be same as the original development class in which the real objects are in.

     

    The deletion transport should have a target assigned so that it generates the K and R files when it is released.

     

    !caution! Make sure you are Importing this transport into "Required" systems and remove it in the import Q once it is imported.!!

    Share and share alike

    $
    0
    0

    invest_in_sharing.jpgSharing your toys

    When you grew up I am sure that you were taught to share your toys with your siblings and your friends. It is certainly a value to that I am keen to instil into my children.

     

    When we share we get access to what we could not have done ourselves. It broadens our experience and gives us exposure to things that we might not have come across ourselves.

     

    This goes for algorithms and implementation as well as the latest kitteh movie you found on youtube.

     

    Being exposed to a varied approaches to development can only improve our abilities as coders. A great way to be exposed to other code is to read books with code samples that are annotated and explained or blogs or open source software.

     

    Github has become of late the goto repository for such projects. It is a great place to discover and learn and also contribute.

     

    Having said this it is sad to hear and read about SAP's approach to such sharing. In response to community enquiry SAP promoted Codexchange based on the SVN source control system and available to developers working on a specially licenced version of the ABAP Netweaver Stack. Codexchange has an uncertain future - it seems to be stuck and not moving forward. The answer to such a problem is simple - use github.

     

    What is the problem with sharing our code on Github? How is it different to adding source code to a blog here on SCN or on another technical site or even in a book?

     

    Github lowers barriers to entry and because all the hip kids hang out there it may even encourage people to get with the grey haired abap gang.

     

    To innovate you need to change and although the ABAP language is changing and introducing new features SAP's approach to sharing is not.

     

    Thoughts?

     

    Thanks to Toban Black for the image


    Tips from an ABAP Instructor

    $
    0
    0

    Calling all ABAPpers!

     

    Veteran SAP Education instructor Peter Walters has just published a series of blogs covering what's new and noteworthy in ABAP, and which courses you might want to take to beef up your skills in those areas:

     

     

    And check out our growing list of blogs from SAP Education’s Instructors, covering an ever-widening range of topics – from SAP HANA to SAP Solution Manager and more.

     

    Happy reading!

    Sarah


    Adobeform for multiple trays

    $
    0
    0

    Adobeform for multiple trays

     

    We have some printers with multiple trays. Depending on the message category, we can print adobeform from a specific tray. Rule will be always as simple i.e. for one message category; we know the tray number where we want to print the adobeform.

     

    First we need, maintain data in TVARVC as below:

     

    Go to Transaction STVARV and maintain the data for the Message Category and Tray Value.

     

     

    1.jpg

    Name contains the Message Category and the Value is Tray Name / Number (i.e. either for Duplex Printer or Simplex Printer)

     

    Before calling the function module ‘FP_JOB_OPEN’, pass the value of the tray name or number in the field ‘JOB_PROFILE’ of the structure ‘SFPOUTPUTPARAMS’ i.e. form processing output parameter.

     

    Code as below:

    Taking example for Production Order (CO01/02/03)

     

    Transaction OPK8

    List Name

     

    3.jpg

     

    Driver Program: PSFC_OBJECT_LIST_PDF

    Enhancement Spot at Form ‘FILL_OUTPUTPARAMS’.

     

    2.jpg

    DATA:        lv_messageo TYPE fpjobprofile,

                      lv_msgo     TYPE rvari_vnam.

     

    CONSTANTS: lc_msg1 TYPE rvari_vnam VALUE 'ZFPP01_',

                         lc_msg2 TYPE rvari_vnam VALUE 'ZFPP02_'.

     

    *-- Concatenate like 'ZFPP01_LG01ZP01'

    *-- print_co-lstid contains value of list name i.e. LG01 and print_co-auart contains the value of Order Type i.e. ‘ZP01’, ‘ZP03’, etc.

    CONCATENATE lc_msg1 print_co-lstid print_co-auart INTO lv_msgo.

     

    *-- Select query to fetch the tray value from table 'TVARVC' comparing the Message Category

     

    SELECT SINGLE low  "ABAP/4: Selection value (LOW)

    INTO lv_messageo

    FROM tvarvc "Table of Variant Variables (Client

    WHERE name = lv_msgo

          AND type = lc_value_p.

    IF sy-subrc EQ 0.

        xs_outputparams-job_profile = lv_messageo. " Populate the value of the Tray name in the field 'JOB_PROFILE'.

    ENDIF.

     

    This is how depending on the message category, print will come from a specific tray in the printer has multiple trays.

     

    Thanks for reading the document.

     

    Regards,

    Debopriya Ghosh

    Enhance "GO TO" Menu in BP(Business Partner) using BDT(Business Data Toolset)

    $
    0
    0

    Business Data Toolset is a easy way to enhance different SAP Business Objects.

    According to SAP Help:http://help.sap.com/saphelp_crm50/helpdata/en/2f/696d360856e808e10000009b38f839/frameset.htm

    All menu options not fixed in the menu can be included in this menu through additional functions. These additional functions can be reached through the following submenus:

    ·         Further object functions (<Object> menu)

    ·         Further editing (editing menu)

    ·         Additional goto destinations (Goto menu)

    ·         Additional extras (Extras menu)

    In this article is an example of  "Go To" menu enhancement on SAP Business Partner.

    1.     Run transaction BUPT

    2.     Then go to Menu: Business partner -> Control -> GUI Functions - > Additional Functions (transaction code BUS9)

    1.jpg

    3. In the open transaction  create new Menu Item:

    a)      a. Menu item description;

    b)      b. Menu to Enhance;

    c)      c. Mode, in which the function is available

    2.jpg

    If we leave “Application” empty, new function will be available from all Business partner lists.

    a)      d. View  in which the new function is  available, empty – available everywhere

    3.png

    e.  e. The same as step D.

    4.png

    Now new function is available fron Menu “GoTo ->  More GoTo Targets”.

    5.jpg

    But we still need to assign functionality to the new menu item

    To do this, perform following steps:

    1.      1.  In transaction BUPT, go to Menu: Business partner -> Control -> Events ->Business Data Toolset (BUS7)

    6.jpg

    1.       2. Choose “FCODE     Separate Function Code”

    2.       3. Copy any of the functions   to your own function.  As example I’ve copied function BUP_BUPA_EVENT_FCODE to the function ZCBP_BUPA_EVENT_FCODE_DOKBS.  And  have written such code. Parameter “I_FCODE” will contain menu name which we have created  earlier.

    7.jpg

    8.jpg

    1.      4. Then we need to fill customizing table from step 3;

    9.jpg

    1.      6. Then we need to fill customizing table from step 3

    10.png

    11.jpg

    Everything works. In my task, from BP other system must be launched ,

     

    By means  of Business data toolset you can extend objects with customer screens, logic and check. All information you can find at - http://help.sap.com/saphelp_crm50/helpdata/en/2f/696d360856e808e10000009b38f839/frameset.htm.

     

    I hope this information will be useful.

    Best regards.

    Pavel Rumyantsev

    Auto generating UML diagrams from ABAP code

    $
    0
    0

    Alternative title - I hate doco

     

    http://www.wombling.com/wp-content/uploads/2013/08/run-from-doco-624x282.png

    It’s kinda a mantra that I live a reasonable part of my life to. I think the above image is a not unjustifiable representation of my feelings about documentation:

    But there are many good reasons that I should be doing doco. Most of them are supposed to save the customer money in the long run. And occasionally by doing the doco, I even find some small errors in my code that I hadn’t seen before.

    InnoJam Sydney 2011

    Before InnoJam had any of that fun fluffy design thinking aspect to it, we ran one in Sydney. It was good fun, and people could build whatever the heck they wanted.

    In staying true to my aversion for writing doco, I came up with an idea about auto-generation UML diagrams from SAP code.

    Here’s a video of the solution we came up with:

     

    here’s a link to the Prezi that I presented in that video:

    http://prezi.com/zri5q-ib4vzp/?utm_campaign=share&utm_medium=copy&rc=ex0share

     

     

     

     

     

    I wrote a blog post about it:

    "Programmers are lazy" - InnoJam them!

    But it was long time ago and the move to a new version of SCN has kinda buggered it up.

    Anyway, in short – Rui Nogueira never managed to get the terms and conditions of CodeExchange changed to a level where I’m happy to support it and put code in there. I’m pretty sure he tried though. So I didn’t do anything with the code.

     

    Fast forward 3 years

    I have a whiteboard in the office covered in post-it notes. They all represent at least one development that I’ve done for the current project I’m working on. At the beginning of the project, I was very good, and did all my doco as I went along. Then the poo hit the fan, and everyone wanted everything done yesterday, and didn’t care about doco.

    So I now have a whiteboard full of post-it notes that represent potentially weeks of doco hell for me. So in my “free time” in the evening I decided to see if I could recreate the solution that we’d built in Sydney, and perhaps make it a little nicer.

     

    UML output

    The first thing I decided, was that I was NOT going to try to build the graphical output myself. Having had lots of fun in Sydney trying to make Gravity do something it really wasn’t designed for I thought I’d research how else I could get my interaction diagrams created.

    If in doubt Wikipedia

    http://en.wikipedia.org/wiki/List_of_UML_tools

    There were loads there, and I’d pretty much decided on UMLet when I discovered something about interaction diagrams. Basically, interaction diagrams are supposed to show interaction between objects. Bit bloody obvious really. However, the thing is, if I’m documenting my code, I’d really like to show the interaction within my objects too. I.e. if I make a call to a private method of my class, I’d really like that to show up in my diagram. Given that interaction diagrams are only supposed to show external interaction it’s not surprising that most of the tools for creating the diagrams don’t really support this idea of internal object calls.

    So a bit of browsing later and I found PlantUML. It has some awesome functionality for creating sequence diagrams, actually, most UML diagrams it seems, but it was the sequence diagrams that I was interested in.

    Here’s a simple example:

    http://www.wombling.com/wp-content/uploads/2013/08/simple_example.png

     

    See how it’s quite possible to show “internal” calls of an instance and also show the life time of those calls. This feature I didn’t find on the other free UML tools that I looked at. There are a bunch of other formatting features that can be used too. If you’re interested check out their website: http://plantuml.sourceforge.net/sequence.html

     

    Intercepting the SAP standard UML generation

    So in transaction SAT there is the possibility to generate your own JNet UML sequence diagram (this exists as standard.)

    http://www.wombling.com/wp-content/uploads/2013/08/press-the-button.png

     

    However, it does not allow you to do things like filter out standard SAP routines (as far as I know! If anyone can tell me how to do this (without needing to list every method I call, please let me know!) When I was looking at one of my examples, where I ran a program to generate a performance review document for an employee, there were over 100,000 different routines called. Only about 400 of those calls involved my code, so you can imagine generating a UML diagram for the whole 100,000 calls would be a bit of overkill (not to mention an impossible to read diagram).

    In customer systems there is a function module  ATRA_UML_DECIDER  that has been purposely handicapped. One does have to wonder why this has been done, but nevertheless it has.  It allows the user to chose from a list of potential UML extraction routines. All of these routines implement the IF_ATRA_UML_TOOL interface. There are classes for extracting to JNet, Borland Together and Altova. Now, I’m sure that Borland and Altova have good products, it’s just that I don’t really want to spend money on then when there are perfectly good (for my tasks) free and open source products out there.

    There is a factory class/method CL_ATRA_UML_FACTORY  that creates an instance of a class implementing the interface. I overrode this method to use my particular extractor if it was me running the code. In the future, I might enhance this to check for a user role, or perhaps a user parameter, that’s trivial, the main point will be to allow others to access this logic too.

    The guts of the code

    Simply my implementation of the interface reads the table of data that is passed to the interface, removes all calls that aren’t to or from custom code and then builds a PlantUML representation of that code.

    Here’s a very simple output that generates the diagram above.

    @startuml
    hide footbox
    autonumber
    participant "Instance 1 of Class\nZCL_HR_EMPLOYEE" as 1
    1 -> 1: Call method GET_HELD_QUALIFICATIONS
    activate 1
    1 -> 1: Call method ZCL_HR_OBJECT->GET_RELATIONSHIPS
    activate 1
    create "Static Methods of Class\nCL_HRBAS_READ_INFOTYPE" as 2
    1 -> 2: Call method GET_INSTANCE
    activate 2
    2 --> 1
    deactivate 2
    create "Instance 1 of Class\nCL_HRBAS_READ_INFOTYPE" as 3
    1 -> 3: Call method IF_HRBAS_READ_INFOTYPE~READ_PLAIN_1001
    activate 3
    3 --> 1
    deactivate 3
    1 --> 1
    deactivate 1
    create "Instance 1 of Class\nZCL_HR_QUALIFICATION" as 4
    1 -> 4: Create instance of class ZCL_HR_QUALIFICATION
    activate 4
    4 -> 4: Call method ZCL_HR_OBJECT->CONSTRUCTOR
    activate 4
    create "Function Group\nRHS0" as 5
    4 -> 5: Call FM RH_GET_ACTIVE_WF_PLVAR
    activate 5
    5 --> 4
    deactivate 5
    4 --> 4
    deactivate 4
    4 --> 1
    deactivate 4
    deactivate 1
    @enduml

     

    A slightly less trivial example

    The following code does some pretty simple stuff, it finds who is my manager, and finds out what required qualifications my position/job has.

    DATA: lo_emp TYPE REF TO zcl_hr_employee,
    lt_managers TYPE ztthr_employee_objects,
    lt_required_quals TYPE ztthr_qualifications.
    TRY.
    lo_emp = zcl_hr_employee=>get_employee_by_user_id( sy-uname ).
    lt_managers = lo_emp->get_position( )->get_managers_recursive( ).
    lt_required_quals = lo_emp->get_position( )->get_required_qualifications( ).
    CATCH zcx_hr_no_managing_pos_found  ” No managing position found
    zcx_hr_no_holders_found  ” no holders for position found.
    zcx_hr_no_position_found    ” no position found
    zcx_hr_user_id_not_found.  ” cannot find user id for employee
    ENDTRY.

     

    So I thought I’d trace it:

    it works out at around 200,000 different routines being called. 62 of those are my code, the rest standard.

    http://www.wombling.com/wp-content/uploads/2013/08/run-through-1.png

    First of all, I need to schedule a trace for myself…

    run through 2

     

    run through 3

     

    Need to know which session I will be recording. If I left it as “Any” it will start recording this session, not very useful!

    run through 4

     

    Session 2 it is!

    run through 5

    run through 6

    actually run it now!

    and the schedule status changes to executed.

    run through 7

     

    I now need to delete the scheduled measurement. Despite the intimidating words, this does not delete my measurement, just the scheduling of it.

    run through 8

     

    now swapping to the “Evaluate” tab in SAT I can see my measurement, and I can click to output to UML

    run through 9

     

    Late breaking discovery! - Filter data using SLAD!

    In a somewhat surprising discovery, I found that I can filter the results of the trace so that only the custom code bits get processed in UML handling routines. This speeds up the processing immensely. HOWEVER! This does mean that you will NOT see where your code calls the SAP standard code, but for some diagramming tasks this would be fine.

    slad filter1.png

     

    slad filter2.png

    Clicking on the button triggers a bit of a pause whilst the system code chugs away and does loads of stuff it doesn’t need to do…

    Then

    run through 10

    Save the data and PlantUML starts converting it immediately:

    run through 11

    and the result:

    example

    Would probably have been a little bigger if my employee had a job assigned to their position, but you can see how incredibly easy this now makes documenting the functionality I’ve built.

    After checking the SCN T&C and finding similar (if not worse conditions for the upload of SAP derivative code (it uses an SAP standard interface, so it probably is)) and the same indemnity clauses that make me refuse to participate in Code Exchange, I've decided not to share the code here. That said if someone else is willing to put the code out there on Code Exchange I'm more than happy to provide it to them. Perhaps someone will put their hand up to load it into Code Exchange - Gregor Wolf ???


    This content was previously published on my own blog, but the tip about the SLAD filter wasn't, and certainly the code wasn't published there. Some excellent points have been raised about how this isn't a replacement for actually documenting the functionality of the code that has been built. But, I have found at least one case where using this tooling has helped me write a better bit of code by sharing references to objects that I didn't realise were being instantiated multiple times. Could something like this help you with your doco? Do you have any thoughts?

    Sending SMS to mobile phones (in the Netherlands)

    $
    0
    0

    I recently got a question about sending SMS text messages from SAP to a mobile phone. My first thoughts went to specialized hardware, but I found out there is a much simpler way to achieve this.

     

    I don't know if this solution is available in the rest of the world, but here in the Netherlands we have a specialized public gateway for this !

    You can activate this gateway by sending a code ("EMAIL AAN") to your network number:

     

    Network number     Provider (site)

    321                       www.hi.nl

    969                       www.kpn.com

    126                       www.orange.nl

    888                       www.t-mobile.nl

    221                       www.telfort.nl

    125                       www.vodafone.nl

     

    After you send this SMS, you receive a message back that the gateway has been activated and it also contains an email adddress (probably something like +316XXXXXXXX@GIN.NL).

     

    From this point on, you can send an email from SAP to this address and the telephone will receive the sender and the subject of this email as an SMS text message ! There is ofcourse always the size limit of 160 characters.

     

    Sending an SMS to your network with code "EMAIL UIT" will switch the gateway off.

     

    There are probably costs for using this gateway, but I don't know what they are.

     

    Furthermore I should say that I only tested it with the Vodafone network, but I am confident that this should work on other networks too. Maybe the on/off codes differ a little bit.

     

    A nice way to send SMS messages without much hassle.

    The issue with having many small classes

    $
    0
    0

    Aftermath

    The previous blog post was about applying design principles to the architecture of a simple parser application, which served as an exercise application. It led to quite a few, small classes in the end which all had their own responsibility.

    Big Picture.PNG

    Issue

    As most of the classes deal with solving a certain aspect of the exercise, one class has the responsibility to arrange the work of the other classes to solve what needs to be solved. This class implements the interface ZIF_OCR_PARSING_ALGORITHM . An instance of that class is given to every consumer which needs to parse some sample input.
    Every low level class that this class is dealing with to “orchestrate” the program flow, is registered as an dependency. Dependencies can be declared in the constructor of this course grained class. This makes creation of that class impossible without satisfying its dependencies.

    methods CONSTRUCTOR
    importing
    !IO_FILE_ACCESS type ref to ZIF_OCR_FILE_ACCESS
    !IO_LINES_SEPERATOR type ref to ZIF_OCR_LINES_SEPERATOR
    !IO_CHARACTERS_SEPERATOR type ref to ZIF_OCR_CHARACTERS_SEPERATOR
    !IO_CHARACTER_PARSER type ref to ZIF_OCR_CHARACTER_PARSER .

     

    Providing dependencies during object creation by an external caller is called “dependency injection” (DI) as opposed to having classes that create their dependencies on their own. As the creation of the low level instances is not in the control of the class that orchestrates the parser flow, this paradigm is also called “Inversion of control” (IoC).
    However, this approach has another challenge ready. That issue has been described very clearly by Oliver as a reply to the previous blog post
    “Injection in the constructor – even to an interface – looks like quite tight coupling to me as it leaves the consumer of the coarse-grained class (the algorithm, in your case) to instantiate the composites.”

    Factory

    Of course the consumer of the composite object model should not create it on its own. One possibility would be to utilize the factory pattern. Instead of composing the main object and all of its dependent objects on its own, the consumer delegates this task to a factory method, which could be a static method call in its most simple realization:

    DATA lo_parser TYPE REF TO ZIF_OCR_PARSING_ALGORITHM.
    lo_parser = ZCL_PARSER_FACTORY=>CREATE_NEW_PARSER( ).

     

    Within CREATE_NEW_PARSER( ) the same magic takes place as before: in the first step, all low level objects are created, in the second step the main parser object is created, while its dependencies declared by the CONSTRUCTOR are satisfied. This approach is called “Poor man’s dependency injection”

     

    *alternative 1: poor man's DI
    DATA lo_ocr TYPE REF TO zif_ocr_parsing_algorithm.
    DATA lo_char_sep TYPE REF TO zif_ocr_characters_seperator.
    DATA lo_char_parser TYPE REF TO zif_ocr_character_parser.
    DATA lo_file_access TYPE REF TO zif_ocr_file_access.
    DATA lo_lines_seperator TYPE REF TO zif_ocr_lines_seperator.
    CREATE OBJECT lo_char_sep TYPE zcl_ocr_characters_seperator.
    CREATE OBJECT lo_char_parser TYPE zcl_ocr_character_parser.
    CREATE OBJECT lo_file_access TYPE zcl_ocr_gui_file_access.
    CREATE OBJECT lo_lines_seperator TYPE zcl_ocr_lines_seperator.
    
    CREATE OBJECT lo_ocr TYPE zcl_ocr_parsing_algorithm
    EXPORTING
    io_character_parser = lo_char_parser
    io_characters_seperator = lo_char_sep
    io_file_access = lo_file_access
    io_lines_seperator = lo_lines_seperator.
    

     

    IoC Container

    However, the task of manually creating dependent objects in order to hand them over to a course grained object when it is created, can be automated. Think of the facts that are known:

    • all low level interfaces like ZIF_OCR_CHARACTER_PARSER have one class that implements each of them
    • instances of these classes could be created dynamically, as their constructors currently require no input parameter
    • the course grained interface ZIF_OCR_PARSING_ALGORITHM also has a specific class that implements it
    • however, its constructor requires some instances of the low level interfaces
    • as these required parameters are instances of the low level interfaces, these dependencies could be satisfied automatically

     

    As a brief summary, this is exactly what an IoC Container should do, also refered to as DI containers.
    The registration of the interfaces and classes is done programmatically in the following example. However, it may be moved to customizing in order to “free” the code of having hard references to explizit class names

    *alternative 2: IoC Container
    DATA lo_ioc_container TYPE REF TO /leos/if_ioc_container.
    lo_ioc_container = /leos/cl_ioc_container=>create_empty_instance( ).
    lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_characters_seperator' iv_implementer = 'zcl_ocr_characters_seperator' ).
    lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_character_parser' iv_implementer = 'zcl_ocr_character_parser' ).
    lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_file_access' iv_implementer = 'zcl_ocr_gui_file_access' ).
    lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_lines_seperator' iv_implementer = 'zcl_ocr_lines_seperator' ).
    lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_parsing_algorithm' iv_implementer = 'zcl_ocr_parsing_algorithm' ).

     

    No matter, how the classes have been registered, either in the code or by customizing, the caller now only needs to call the container and request an instance from it at startup:

    DATA lo_ocr TYPE REF TO zif_ocr_parsing_algorithm.
    *create the container either by reference to customizing or by the registration coding shown above...
    *...
    lo_ocr ?= lo_ioc_container->get_implementer( iv_contract = 'zif_ocr_parsing_algorithm' ).
    Viewing all 948 articles
    Browse latest View live


    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>