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

Flexible MS Excel documents with ABAP/4

$
0
0

I had a  dream. Imagine document designers who don’t need to ask developers how to  create or change an output layout of documents. Imagine developers who have one  simple tool to work with MS Office documents. Imagine there’s no need to keep  design and data extraction together. Suppose you can change your corporative  design in documents within minutes and you don’t need to use developers for  this purpose!
  Imagine you  can create a scheduled task that will send MS Office documents as attachments.  There’s no need of MS Office installed! There’s no need of GUI sessions. You  can manipulate documents from WDA, from background and there’s no more limits  of MS Office use.
  It was a  perfect dream. Since that time I’ve created a tool to fill MS Office documents  by names. But it needs GUI and MS Office installed. Then I’ve learnt  XSL-transformations and found them pretty fast and simple. But you need to  create a transformation each time you have document design changed. Furthermore  you can’t keep images within Excel files created by XSLT. It was a nightmare  when I got an issue to create an Excel document with graphics from WDA!
  Then I  found a tool abap2excel. It’s quite nice. But it’s quite huge and I didn’t find  there functions to use range names to fill data in a template file. Almost  forgot, I know a functional provided by Parazit to work with MS Office  documents.
  I have to  tell you honestly that I didn’t learn the tool abap2excel as soon as I had just  2 days to complete my task.
  Any task is  a challenge and I don’t like to tell “It’s impossible”. So I learnt a structure  of MS Excel document to fill desired sheet with desired data.
  But then I  got the new issue to create a report that sends SAP Query results by e-mail. Of  course this report should avoid GUI sessions as soon as it’s for background  tasks. Of course it would be nice to separate design and data extractions for  this task because someone can change his or her mind and redraw the document  template.
  So here are  tasks that I solved by my functionality:

  • Let  users store a template with corporate elements such as images
  • Designers  should have a way to describe where data appear
  • Developers  should have a simple interface to manipulate MS Excel document
  • Utility  should be able to work without GUI and without MS Office use

The  business process is very simple: Power user decides how the document will  appear and then Power user defines named ranges:

image002.jpg
  Here you  can see a common design of the document. It consists of:

  • Logo  image.
  • Texts  that can be common for any document with this template. Note texts can appear  at any place of the document. So if data table is to be inserted these texts  should appear correctly under the table. It means if data table has 3 lines the  footer text will appear in 13-th line.
  • Defined  named ranges. Here you can see the named range ‘HEADER_LINE’. This document  keeps ‘DATA_TAB’ named range also.
  • Two  additional sheets. These additional sheets are optional of course.

The idea of  my tool is to retrieve defined names of the document and then place data  correctly. For example, if I used this template to output table with four  columns they should appear in the same manner as it is for the first column. Here  is the example image:

image004.jpg
  But there  can be some situations when the document is static. For example, you wish to  create a table of top ten sold positions and it will look exactly in the same  way regardless data displayed:

image006.jpg
  If the  named range holds more than one row, it’s hard to determine which of these  styles to copy. When you’ll try to fill this ‘DATA_TAB’ named range with 5  lines with 4 columns, you should get next image:
  image008.jpg
  Now it’s  possible to work with templates stored either at web-repository (t-code SMW0)  or passed as xstring stream. The result is xstring. Here is an example code to  fill the document:
  DATA: lr_xlsx TYPE REF TO zcl_bas_xlsx_utils,
        lt_data TYPE TABLE OF e070,
        lv_binlen TYPE i,
        lv_xstring TYPE xstring,
        lt_content TYPE sdokcntbins.
 
  CREATE OBJECT lr_xlsx
    EXPORTING
      iv_template = 'ZBAS_SQ01_TO_XLSX_EXAMPLE'.
  SELECT * FROM e070 INTO CORRESPONDING FIELDS OF TABLE lt_data.
 
  lr_xlsx->put_table_into_name(
    iv_name    = 'DATA_TAB'
    it_table = lt_data ).
 
  lv_xstring = lr_xlsx->get_file( ).
 
  CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
    EXPORTING
      buffer          = lv_xstring
    IMPORTING
      output_length   = lv_binlen
    TABLES
      binary_tab      = lt_content.
 
  CALL METHOD cl_gui_frontend_services=>gui_download
    EXPORTING
      bin_filesize              = lv_binlen
      filename                  = 'C:\Temp\Temp.xlsx'
      filetype                  = 'BIN'
    CHANGING
      data_tab                  = lt_content
    EXCEPTIONS
      others                    = 24
          .

And finally here is the reference to the source code. There are text files zipped. I’ve  tried to minimize code.
  Hope you’ll  enjoy it.
  Actually there is one  issue for this functional: when you open result file it reports an error  message. I’ll be happy if  anyone can solve it. Warning disappears if I open one of xml files of the Excel  zip structure and just re-save it.

 

Here you can find dictionary types description you probably need:

types mdp_tt_xml_docref type table of ref to IF_IXML_DOCUMENT.

types:

begin of ZBAS_ALSMEX_TABLINE,

          row type KCD_EX_ROW_N,

          col type KCD_EX_COL_N,

          VALUE type STRING_UNICODE,

end of ZBAS_ALSMEX_TABLINE,

        ZBAS_ALSMEX_TABLINE_T type table of ZBAS_ALSMEX_TABLINE.


Ensuring Program documentation with ABAP editor capabilities

$
0
0

As per the ABAP programming standard of clients, we have to always maintain header text, Insert , Replace, Comment and Delete  comment lines  for future reference.  If the program inline documentation is not maintained, it will always be an issue reported during code review process.

If we could maintain code template, we can just use keyboard shortcuts to  reuse the comment lines required as shown below.  This approach has helped us implement coding standard in an easier way.

First go to ABAP editor ( THis feature is availble in ECC6.0 )

editor tool.JPG

then create Code Template as per our inline documentation standard.

editor tool.JPG

Here we use a template for the text. This could be a text that is used for marking modifications or Definition and implementation of  new program changes.

* Begin of Insertion <username> <Date of Change>
* End of Insertion    <username><Date of Change>

editor tool.JPG

editor tool.JPG

In this way we could ensure that all developers in a team are using the same way for inline documentation
and documentation time can be considerably reduced.

 

Understanding the basic flow of a program

$
0
0

I was going through the se93 transaction and came across Utility-->Call Graph

This Give us the hierarchical view of the source code. As an example go to se93 and put in a transaction code and click on Utility-->Call Graph.

 

se931.png

The following screen would come.

se932.png

Clicking on the + will expand  as follows

se933.png

The initial screen for migo is 001. By expanding each of the nodes we can see which all modules are there. Double clicking on the module takes you directly to the screen painter and you can understand further.

se934.png

 

This gives us a basic understanding on how the program flow is. This may not work for parameter transaction.

Issues with copy window in Smartform and its solutions.

$
0
0

Issues with copy window in Smartform and its solutions

Scenario :We got the scenario in which client wants 4 copies of the form with 4 different texts  or multiple copies of smartform .

As we all know,  this can be easily handled by using the copy window in the smartform.

Issue :But some times the concept of copy window does not work. And we get the various copies of smartform with the same content. That is 4 copies of same content and not the 4 different copies with the different content .

Analysis :This issue happens due to  a program error, the system no longer reads any of the parameters that are saved in the table TSP02A. This problem occurs due to the Kernel . Due to this kernel  error, various parameters that are set in a Smart Form does not work. Following are impacted areas.

 

1.     In a Smart Form, you can use a window of the type "Copies Window". You can use this to control that the system outputs different texts in the various copies of the form. Due to the kernel error, the system prints the various copies with the same content.

 

2.     In the print parameters, you can set the "Group (1-1-1, 2-2-2,...)" indicator. The system ignores this indicator.

 

3.     If you use the Zebra device types mentioned in SAP Note 750002(Support for Zebra label printer (ZPL2))/750772(Information about the ZPL-II printer driver for Smart Forms), you can set attributes such as, S_LZPL_SETUP in the Smart Form using a command node. These no longer work.

 

There are 2 solutions for the same issue.

 

 

Solution  1 :Check the kernel level in the system.

 

Step  1 :  Goto  transaction SM51.

Step  2 :Click on the ‘Release Notes’.

1.png

Step 3 :Check the kernel patch number .

1.png

 

For removing the error, apply the corresponding sap patch levels corrected kernel as specified below. And use the copy window to print the different data on different pages.

 

Software Component Version

Support Package

SP Patch Level

SAP KERNEL 7.20 32-BIT

SP215

000215

SAP KERNEL 7.20 32-BIT UNICODE

SP215

000215

SAP KERNEL 7.20 64-BIT

SP215

000215

SAP KERNEL 7.20 64-BIT UNICODE

SP215

000215

SAP KERNEL 7.21 32-BIT

SP007

000007

SAP KERNEL 7.21 32-BIT UNICODE

SP007

000007

SAP KERNEL 7.21 64-BIT

SP007

000007

SAP KERNEL 7.21 64-BIT UNICODE

SP007

000007

SAP KERNEL 7.21 EXT 32-BIT

SP007

000007

SAP KERNEL 7.21 EXT 32-BIT UC

SP007

000007

SAP KERNEL 7.21 EXT 64-BIT

SP007

000007

SAP KERNEL 7.21 EXT 64-BIT UC

SP007

000007

 

Solution  2 :Generate multiple  smartforms  in a single spool request.

 

For this use two function modules ‘SSF_OPEN’ and ‘SSF_CLOSE’.

 

SSF_OPEN is used for triggering the output device like printers. 

SSF_CLOSE is for closing the output device application.

 

Following is the general code .

 

First call the FM ‘SSF_OPEN’.

    CALL FUNCTION 'SSF_OPEN'
      EXPORTING
        user_settings           = ' '
        output_options         = ls_outopt     ” SAP Smart Forms: Smart Composer (transfer) options            
        control_parameters  = is_ssfctrlop  “  Smart Forms: Control struct

      EXCEPTIONS
       
formatting_error       = 1
        internal_error           = 2
        send_error               = 3
        user_canceled         = 4
        OTHERS                 = 5.

 

Then call the smartform in the loop for the number of copies we want.

 

IS_FMNAME is the FM name generated from smart form from FM 'SSF_FUNCTION_MODULE_NAME'.

 

WHILE l_copy_count < 4 .
      l_copy_count = l_copy_count + 1.

 

lv_copy_count  contains the count of the copy .

      

CALL FUNCTION  is_fmname
        EXPORTING          control_parameters  = is_ssfctrlop
          output_options         = ls_outopt
          lv_copy_count          = l_copy_count

       EXCEPTIONS          formatting_error       = 1
          internal_error           = 2
          send_error               = 3
          user_canceled         = 4
          OTHERS                  = 5.

ENDWHILE

 

Use the secondary window to print the differentiated text on the basis of copy to be printed on the basis of l_copy_count data .

 

 

And then use the FM SSF_CLOSE to close the output device application.

 

Call 'SSF_CLOSE'
    CALL FUNCTION 'SSF_CLOSE'
      EXCEPTIONS        formatting_error     = 1
        internal_error         = 2
        send_error             = 3
        OTHERS               = 4.

 

convert word document into PDF via Adobe Livecycle Enterprise service

$
0
0

Hi Friends,

 

currently I am working on a customer project regarding the usage of SAP document builder. Document builder is an ABAP webdynpro application which could allow you to main template part with various document type ( html, txt, pdf, docx etc ) into a final document with again various document type. You can find more information of it via scn wiki here.

 

You can play with it by starting web dynpro application /IPRO/WD_DOCB. In preview tab there are several output types. 1 means the default docx format and 11 means PDF format ( via LiveCycle ES).

clipboard1.png

By default only docx output is supported by SAP, the PDF output is just provided via consulting note "1154999 - Using Adobe/LiveCycle ES to Render Docb Document to PDF". As the PDF output is fundamental for customer, I have to make it work. Unfortunately the consulting note itself just contains the common development steps like how to create ABAP consumer proxy class, how to maintain logical point and RFC destination, but not for Adobe Livecycle Server configuration and deployment etc. If you are interested with this topic, please continue to read.

 

Although this blog is talking about document builder but actually the idea could be used generically in other case: as long as you have a binary content of a word document, you can consume Adobe LC ES to convert it to PDF in your application.

 

You might ask that you can google "PDF conversion" and there are plenty of open source solutions and free web services to do the job. However, the customer I am workign for is the Top one in its area in China with government background. Take security into consideration, can you imagine they can accept their highly confidential document is sent to a public server which is not deployed within their landscape? You can also googled many undocumented command usage regarding Acrobat Pro which can silently do several operations like print without UI. I didn't try that since the undocumented means if I have any issues, I could not get any official support from Adobe.

 

1. Of course the Adobe Livecyle ES is not for free. You must buy license from Adobe. This blog is written based on a trial version called “JBoss (window version)”, which you can download freely from Adobe website below.

 

1.png

2.png

 

2. You can find detail installation steps from http://helpx.adobe.com/livecycle.html:

3.png

 

For normal usage just choose “Turnkey”.

4.png

 

The installation took about twenty minutes in my laptop with 4G memory, Inter(R) Core(TM) i5 CPU 2.53GHz, win7 64bit OS.

 

1.     3. Once installation done, log on with administration console with http://localhost:8080/adminui. The default user/password : administrator/password.

For security reasons please change default password immediately.

If you can see the following screen, it means your installation is successful.

5.png

 

If you encounter any errors please check the installation logs. The path is:

"C:\Adobe\Adobe LiveCycle ES4\Adobe_LiveCycle_ES4_InstallLog.log" in my laptop.

 

1.    4. Configure JBoss server as a service in OS. You can find how to do this in document in path "C:\Adobe\Adobe LiveCycle ES4\jboss\bin\README-service.txt". If you are installing the trial version on your laptop, it is recommended to set the service startup type as “Manual”. Or else every morning when you launch your laptop, you must have a cup of coffee because you can really use it.

 

6.png

 

The server consumes 1.9G memory in my laptop:

 

7.png

1.     5. Do some health check on server. Click home->services->LiveCycle PDF Generator ES4:

 

Click “Create PDF”:

 

8.png

Load any document with type other than PDF to try:

 

 

9.png

You may meet with following error messages:

1.10.png

In fact the JBoss server uses Adobe Acrobat XI Pro to convert the document into PDF format.

The error message prompts you that either Acrobat XI Pro is not installed or there is something wrong with environment variable set up. Don’t mix Acrobat XI Pro with Acrobat Reader XI!
Acrobat has far more powerful functionalities than the latter:

 

11.png

You can also download Acrobat XI Pro trial version from here:

12.png

Install and maintain the environment variable:

13.png

 

Also never forget to set the printer ADOBE PDF as default printer:

14.png

Then retry PDF creation and you should see the following screen:

 

15.png

And you can also see successful information in server.log:

16.png

It is always a good practice to analyze server.log ("C:\Adobe\Adobe LiveCycle ES4\jboss\server\lc_turnkey\log\server.log" ) if you encounter any issues.

1.     6. Import the docb_assmwml.lca attached in the note.

In admin console Home->Services->Applications and Services, ensure the status of docb_assemwml is running:

17.png

1.     7. Create the consumer proxy class in ABAP system as usual. Implement the BAdI by copying the code from note attachment. The code could not be directly activated since the generated data proxy used in the code does not exist in your own system. You must manually replace them with your own data proxy.

18.png

You can find your own data type via SE80, expand Data Types:

19.png

Find the type ends up with BLOB, that’s it.

 

20.png

 

Replace all “zasmblog” in sample code with it. After that you can activate the code.

 

1.     8. Test the application.

The lo_srv is instance of your consumer proxy class.

21.png

The request variable ls_invoke_request contains the binary content of original document in docx format, the response variable contains the converted binary content of PDF format, if successful. In case the conversion fails, you can again check the server.log to find root cause. Alternatively you can also use tcode SRT_UTIL to check.

 

22.png

ICM_HTTP_CONNECTION_FAILED: please firstly check whether your server is available, secondly check whether your RFC destination to server works or not:

 

23.png

 

ICM_HTTP_TIMEOUT: this error occurs occasionally in my testing since I install the server in my laptop and it took the server a huge time to try to convert a complex docx file into PDF format. If you also meet with this error, try to enlarge the time out threshold setting in admin console.

 

HTTP Code: 500 ( Internal Server Error ): it indicates some exception raised from server side when doing the conversion job.

For example: the below detail explanation gives you a hint that the environment for Acrobat XI pro is not set properly.

24.png

And this error information demonstrates that the requested service “docb_assemwml” could not be found in server side. Either check in server side whether the status of it is running or check the name of service is correct.

 

25.png

If everything runs well you will see final PDF displayed.

 

26.png

 

You can also save the file locally and open it with any PDF reader. If the pdf is converted via trial version of Adobe Livecycle ES, you will see the corresponding water mark on it.

 

27.png

Summary: if you reach here I really appreciate your reading on this blog. Adobe LC ES has far more powerful functionalities and the feature in this blog is just a tip of iceberg. You can explore more in Adobe website. My team is responsible for Document builder in SAP. If you have any questions regarding it, just feel free to contact me.

Mild Rant: SAP Notes are getting harder to find

$
0
0

When I'm not on a project, or racking up points in Coffee Corner, my day job is SAP technical support at a government office.

 

Often, a support issue presents itself as a weird error message that alarms the users. See exhibit A (below).

 

note2.JPG

 

In the old days, my strategy for resolving this issue would be:

 

  • Do a little debugging to determine the message id and number ("PSCD_FACTS_EN" and "020": Entry too long. Type &1 allows max. &2 characters)
  • Go to the 'Search for SAP Notes' screen and type in that exact message code ("PSCD_FACTS_EN020")
  • Find the right note - pretty much immediately - and implement it.

 

Problem solved, and I'm finished in time for morning smoko.

 

Not any more!

 

  • A search for 'PSCD_FACTS_EN020' turns up nothing.
  • A search for 'PSCD_FACTS_EN' turns up many notes, none of which match the issue I'm investigating
  • I read through each note, looking for something similar. This requires a fair amount of 'fuzzy' logic
  • I find a note that mentions 'length of character'. Hmm, looks promising, but we already have it.
  • By luck, I notice that there are two additional SAP notes which correct this note
  • Bingo! One of them contains the text : "Entry too long. Type LANGUAGE allows max 1 characters."

 

note.jpg

 

OK, maybe it's partly my bad - I could've debugged further to the method where the error is raised, and searched on that as well. But does it really need to be so hard? WWGD (What would Google do?)

 

My question / rant  / challenge to SAP Support is:

 

If a note is created to fix a specific error message, shouldn't it be searchable by that message (PSCD_FACTS_EN020) , or at least by the message class (PSCD_FACTS_EN)?

 

It would make our job a lot easier.

 

I'm trying to be specific & constructive here, but in general I think that SAP Support Notes are getting harder to search. It seems I just have to keep trying before I hit the jackpot.

 

Thanks for listening. I feel better now.

SAP 7.30 - Theme and Colors

$
0
0

Hello,

 

Just wanted to share a few things concerning SAP 7.30.

 

First of all, there exist a new theme called Corbu Theme in SAP 7.30.

 

Option Icon > Options

 

1.jpg

 

And choose the Corbu Theme, you'll get the new icons, etc.

2.jpg

 

Personally, I don't like the Corbu Theme because of the gray color. Fortunately, there is another nice option.

 

Choose the SAP Signature Theme > Check Use Icons of Corbu Theme

 

3.jpg

 

Thus, you can keep the colors which you want, but use the new fancy icons.

 

 

Also, another tip for those who do not know yet. You can define colors for each environment. This is how you do it.

 

Log in the desired environment.

Option Icon > Option

Color Options > Color in System

 

3.jpg

 

Also, if you want to define new colors, go to Define Colors on the left side and use the slidebar > Save as (a new color).

 

5.jpg

 

Thanks

Six kinds of debugging tips to find the source code where the message is raised

$
0
0

I would like to share with you my debugging tip in my daily life, which makes my life much eaiser. In case you found your own tip are not listed here, please kindly comment it so that it could benefit more people here

 

I will use a simple example to demonstrate. Input an invalid name in SE38 and click display button. An message is displayed in the bottom. I will show you how to find the exact line of code which raises this information.

 

clipboard1.png

 

note: some of the approaches listed here might not be efficient for this very case, I just list all of my tips here for completeness. I do believe each tip could be useful in certain case.

 

approach1:

 

click on the green icon and we can find message class ID: DS, number 017

 

clipboard2.png

 

SE91, use where use list:

 

clipboard3.png

 

OOPS, so many hits...

 

clipboard5.png

 

Then I have to manually fitler them one by one to find the correct one. Double click one by one. I ignore all entries with type MESSAGE E since in my case the message type is not E, but S. After one minute I confirm the following one is the one I try to find.

 

clipboard6.png

 

yes it is confirmed by debugging:

 

clipboard7.png

Summary: the drawback of A1 is that as you see, if there are many where used list results say a hundred, it still takes you some time to manually find the correct one.

 

Approach2:

 

type /h in command area, and click display button to trigger debugger.

 

clipboard8.png

Create a watch point with below two conditions. After that click F8, the debugger will stop automatically at the correct line you want. This approach just took me 20 seconds to finish the job.

 

clipboard9.png

 

Approach3:

 

Launch the debugger just the same as A2, create a dynamic breakpoint with ABAO command = MESSAGE. The debugger will again stops at the correct line.

With this approach again I only spent 20 seconds.

 

clipboard10.png

 

Summary: if the scenario you want to debug is quite complex, for example deep callstack with several components involved, the debugger might stops at the ABAP code with MESSAGE keyword frequently. You must still manually check whether the code is just the one you are looking for at each stop. However it is still much more efficient than you debug manually one by one.

 

Approach4:

 

Tcode SE93, find the package name of SE38:

 

clipboard11.png

Then use report RS_ABAP_SOURCE_SCAN and maintain the search criteria below. The reason why I do not use program name RSABAPPROGRAM is that it is just a wrapper report. The actual implementation of SE38 is not put within it.

 

clipboard12.png

We only have 4 results.

 

clipboard13.png

You can use an alternative tcode CODE_SCANNER which can achieve the same result:

 

clipboard14.png

clipboard15.png

From my point of view, I do like this A4. I can not remember how many times it has helped with my debugging life. What's more, it would be used not only as a debugging tip, but also one way of studying other people's code. Suppose you are curious of how a certain function is implemented by a software component, it is a good starting point to think of a meaningful search keyword and specify the package of that software component and go deep into the result code.

 

Approach5:

 

tcode SAT, create a new variant, ensure the radio box item "Aggregation - None" is selected.

 

clipboard16.png

 

Then launch the SE38 within SAT by clicking "Execute" button.

 

clipboard17.png

 

repeat your steps as usual - input an non-exist report and click display button to see the message. After that click back or quit button in SE38. SAT will automatically be opened to show you all runtime trace information. It will take some time - you can see the progress information in the bottom:

clipboard18.png

click tab "Call Hierarchy", click find button. Input Statement = MESSAGE and go. You will see two search results.

clipboard19.png

double click on the hit list row and then you can see the source code.

 

Summary: if the scenario you are tracing with SAT is complex, you will get a huge trace file.  Although you can specify an extremely big size in trace file, according to my real experience, you will fail to open the result trace file when it exceeds 1 G, at least in my application server. 

 

clipboard20.png

Approach6:

 

First you open SE38, type and invalid program name.

Open a second session, switch on your ST05 with default settings.

Go back to your SE38 window, click display button.

Go back to your ST05 trace, deactivate and display trace result:

 

We know that the PROGDIR table stores the header information of report. It makes sences for ABAP editor to check whether the program name is valid by search it in that table, doesn't it? So the undoubted next step would be the raise of a message if database search fails. Click the "Display ABAP call location" button to go to the source code:

 

clipboard21.png

We see the logic is that first try to search in DB with inactive version, if failed, try with active version.

 

clipboard22.png

So hopefully the code we are trying to find is just in the very neighborhood of the SQL statement in line 774 and 779. Fortunately in this case, yes it is in line 813.

 

clipboard23.png

 

Summary:

 

In my previous work I used to struggle with some tricky case where I don't know how to start my debugging at all. The A6 I call it "ST05 weapon" do prevent me from working over late into the night. Even for the most sophisticated application, I can switch on ST05, repeat the application again, and analyze the trace result to judge which line is useful to start debugging. So I like this overwhelming tool. You may say that it would not help if there is completely no database access for the application to be debugged. Well I would say that would be a rare case at least in my own working area.

 

Do you have additional tips not included in this article? Please kindly share with us


Dark is the Sun ; Bright, the Sky

$
0
0

Frank Computer 02.png

 

Dear All,

 

First off this is very, very, technical, so unless you are in love with the minute details of ABAP programming you might not be that interested. Luckily, lots of people have just such a love affair.

 

In a previous blog

 

http://scn.sap.com/community/abap/blog/2013/10/29/abap-objects--iwtb--part-04--the-curse-of-frankenstein

 

I talked about how Australian SAP expert Chris Paine had asked if I could pass in an array of values as opposed to having ten input parameters.

 

This is very common in programming world the best example is the MESSAGE statement in ABAP where SAP have decided that four parameters are all you will ever need, and you have MSGV1 to MSGV4. As an aside I find it a daily source of hilarity that in SAP you have about six message structures, all different so you have MSGTP, MSGTYP, TYPE etc, all for the same thing. It is fairly clear that the word "REUSE" had never been heard of at SAP interanlly, at least in the past.

 

So, in the blog above I describe creating a ZCL_BC_PARAMETER class where using recursion I can pass in one to a million parameters, howevere many I feel like, all of different types.

 

That worked, all was good, but I was not happy Jan (that reference would make no sense outside Australia).

 

I was not happy because I had tied my PARAMETER class to my LOGGING class and they were "tightly coupled" (oh-err missus) so a change to one might mean a change to the other and you could not use the PARAMETER class on it's own in another context. All the OO books that I read say this is a Bad Thing.

 

I could not rest easy in my bed until I had a solution to this. So, yesterday, as I came home on a Saturday night, as drunk as drunk can be, I saw a solution inside the computer, where my own solution should be. So I called my wife and I said to her, will you kindly tell to me, who owns that solution inside the computer where my own solution should be?

 

"Oh, your drunk, your drunk, you silly old fool, still you cannot see - that's an ITERATOR that my mother sent to me"

 

Well it's many a day I travelled, a hundred miles or more, but a proper iterator in SAP, I never saw before.

 

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

 

Now, an iterator just does something rather akin to looping through an internal table full of different types of things. The problem is an internal table is supposed to consist of lines that are exactly the same e.g. a structure such as VBAP. You don;t have the first line looking like VBAP and the second line looking like LIPS.

 

I have passed in a lot of random things, different each time. Luckily this is not a probelm in ABAP due to the so-called dynamic duo, oh sorry that's Batman and Robin, I mean the so called dynamic data objects.

 

Before I started I thought to myself - I am sure I have seen the iterator pattern used in standard SAP, it's used in ABAP2XLS as well. Why don;t I go looking for an iterator interface. I do a search and sure enough there are twenty billion, all slightly different. Clearly every time there is an internal project in SAP to add a new function that needs an iterator (or whatever) the developer is horrified at thought of looking at what went before, and makes up there own new one, and then calls it a name specific to their project e.g. CL_SCOTTISH_TREE_EATING_ITERATOR to make sure no-one can ever re-use it in a future project. Then they go off and give us "customers" a stern talk about how re-use is the be-all and end-all of life.

 

So, sod it, I'll write my own. My current use case is sending different sorts of values into the application log, but another use case I can think of is having an exception class where I can pass in three values, or five values, or however many I want. That way I don't have to write a different exception class each time with a set number of input paraemeters with concrete types.

 

As we have seen, I have the calling program send in a random number of parameters - the below is far more verbose than you would probably want, I am just hammering home a point.

 

CALL METHOD do_something( io_input_parameter = zcl_bc_parameter=>get( value = ld_i_am_a_string

                                                                          predr = zcl_bc_parameter=>get( value = ld_i_am_a_quantity

                                                                          prder = zcl_bc_parameter=>get( value = ld_i_am-a_money_value ) ) ) ).

 

Then, inside the DO_SOMETHING I need a way to "unpack" the values that I have just been given. I am going to put all the instances of the parameter class passed in into a static internal table.

 

METHOD unpack.

 

FIELD-SYMBOLS: <value> TYPE any.

 

IF if_i_am_first = abap_true.

  CLEAR mt_parameters."Static table

ENDIF.

 

APPEND me TO mt_parameters.

 

CHECK mo_predecessor IS BOUND.

 

mo_predecessor->unpack( abap_false).

 

ENDMETHOD.

 

This is using the recursive call I described before to loop trhough all the parameters passed in and out them in an internal table.

 

Now, it is quite clear that ABAP wishes it was Java, despite Java being owned by Oracle, and every release of ABAP brings the two languages closer together Given that, I might as well use the Java type synatx when dealing with iterator behaviour as oppsoed to evil behaviour like looping over an internal table. Who would do such a horrible thing? That is so ten minutes ago.

 

METHOD has_next.

 

IF mt_parameters[] IS NOT INITIAL.

  rf_yes_it_does = abap_true.

ELSE.

rf_yes_it_does = abap_false.

ENDIF.

 

ENDMETHOD.

 

METHOD next.

 

DATA:lo_parameter TYPE REF TO zcl_bc_parameter.

 

READ TABLE mt_parameters INTO lo_parameter INDEX 1.

 

IF sy-subrc <> 0.

  CLEAR ro_value.

ELSE.

  ro_value = lo_parameter->mo_do_value.

  DELETE mt_parameters INDEX 1.

ENDIF.

 

ENDMETHOD.

 

There we go, the parameters class is now totally independant of the logging class. I told Donna Summer - she was very impressed and told me "this state of independence will be, this state of independence will be".

 

OK, in my logging class I have passed in an IO_INPUT_PARAMETER an instance of my parameter class.

 

io_input_parameter->unpack( abap_true )."i.e. clear static table

 

WHILE io_input_parameter->has_next( ) = abap_true."oh, matron, just like Java

 

lo_do_value = io_input_parameter->next( ).

 

ASSIGN lo_do_value->* TO <value>

 

CHECK <value> IS ASSIGNED.

 

add_input_to_log( EXPORTING id_input = <value>

                              CHANGING  cd_message = ld_message ).

 

ENDWHILE.

 

There we go. The dependencies have been removed, I am sure the "gang of four" who wrote the book on design patterns would be really pleased with me if they were not too busy bursting into flame and turning invisible and extending their arms to twenty foot long.

 

Just to lighten the load of heavy techincal ABAP I would just like to leave you with two totally unrelated things. Firstly, here is an SAP job advert that was emailed to me from Germany the other day. This company, based in Munich, is looking for SAP experts. I have to say you don't get SAP job adevrts like this in the UK or Australia.

 

German Job Advert.jpg

 

On an even more unrelated note, before I became an Australian citizen, I lived in the West Country of England, near Bristol. I would just like to introduce the world to a pop group from that next of the woods who have been going strong since the 1960's.

 

http://www.youtube.com/watch?v=v2CF8HHbWdo

 

This is the Wurzels version of Pulps' "common people". The lyrics are almost exactly the same as far as I can tell, but I think there are more farm animal noises than in the original. The accent of the lead singer reflects the way I spoke when I was young, I had to learnt to tone this accent down or no-one would be able to understand a word I said.

 

Anyway, that is it for now, time to go down the pub.

 

If anyone woud like to tell me what is wrong with my PARAMETER class as described above, or tell me how to achieve the same thing in a better way, I am all ears.

 

Cheersy Cheers

 

Paul

Single step debugging on Macro

$
0
0

Hi Friends,

 

I use a quite simple example to demonstrate:

 

clipboard1.png

 

Execute the report, debugger will stop at line 23.

 

Click this button:

 

clipboard2.png

clipboard3.png

 

The debugger will automatically show tab as below:

 

clipboard4.png

 

then we can execute the macro code one by one by clicking F5:

 

clipboard5.png

 

the line 22 and 23 just represent the ABAP code CHECK strlen(&1) >= 5.

We observed after macro line 22 and 23 are executed, our internal table lt is still empty. This is working as expected, since now the APPEND statement in Macro code is not executed yet.

 

clipboard6.png

 

The internal table is filled after Macro code 27 is executed.

 

clipboard7.png

 

before Macro code line 28 is executed, <first> is not valid, since the respective ABAP code READ TABLE &2 ASSIGNING <first> INDEX 1. in line 14 is not executed yet.

 

clipboard8.png

 

click F5, now <first> is filled.

 

clipboard9.png

 

After Macro code 33~35 is executed, the string concatenation is done.

 

clipboard10.png

 

Although you can debug Macro this way, it does not mean you should not think twice when you use Macro in your code. There are already plenty of discussion in SCN regarding how to use Macro wisely. Hope this blog helps you when you have to debug some complex Macro in legacy code.

Extended Factory Design Pattern in ABAP

$
0
0

Factory pattern is one of the creational design patterns in object oriented design. This blog explains one of the approaches on how this pattern can be extended and implemented in ABAP Object Oriented Programming (OO).

 

In factory pattern, different implementations of an interface are instantiated based on a configuration.  Now, let us take a case where there are multiple implementations for multiple interfaces. The factory class would now have to choose specific implementation for a specific interface.

 

You can find an example class diagram in the file "factory_pattern.png".

 

E type configuration table

Interface_Name

Class_Name

IF_GENERIC

CL_SPECIFIC

IF_GENERIC_1

CL_SPECIFIC_1

 

 

 

C type configuration table

Interface_Name

Class_Name

IF_GENERIC_1

ZCL_SPECIFIC_1

 

 

 

 
 

 

Algorithm within the class “CL_CLASS_FACTORY”

  1. Based on the interface, read customer’s configuration table to get the class name.
  2. If class name is not maintained in (1), read the configuration from SAP delivered configuration table to get class name from interface name.

For the above example, for interface IF_GENERIC, class CL_GENERIC is picked up from the configuration table whereas for the interface IF_GENERIC_1, class CL_GENERIC_1 is picked up from the configuration.

Conceptual differences between OO ABAP and JAVA

$
0
0

I have been associated with JAVA and ABAP worlds from the start. Object Orientation in programming has gained more and more importance. OO ABAP and JAVA are object oriented programming styles. ABAP/4 was extended to support OO concepts. Object oriented concepts of Java, C++, etc... are adopted in OO ABAP and some features which are specific to ABAP; it  means there are some features in ABAP which are not supported in JAVA and vice versa. One more important aspect to consider is ABAP language is meant for business software development.

 

Similarities:

  Deadly Diamond of Death – Both OO ABAP and JAVA allow only one class extending/inheriting option.

   Inheritance, Polymorphism, encapsulation,

 

Differences:

 

Overloading is not supported in OO ABAP

Constructor concept is handled very differently in OO ABAP

Classes/Interface in OO ABAP along with attributes and functions have events. Event concept is important feature of OO ABAP in which the one object raises event; based on the requirement; client objects can subscribe to it.

Biggest difference as per my knowledge  - Collections(ArrayList, Vector, LinkedList, Queue, Hashtable, TreeMap, LinkedHashMap, LinkedHashSet, TreeSet) play important role in Java; no equivalent in OO ABAP. Internal tables and work area can be used whenever we need collection i.e. to process basket of data.

 

 

I know above listed is points are few; will try to put some more points as and when I come across them.

Resumable exceptions

$
0
0

Resume code execution after an exception has been thrown can be very useful in several different scenarios (i.e. processing multiple materials and you want to process all but skip only those where there's an error)

 

This can be solved quite easily using resumable exceptions

 

First we define our exception class (can be global - defined in DDIC or locally defined). Nothing special needs to be specified here.

 

CLASS lcx_my_exception DEFINITION  INHERITING FROM cx_static_check.  PUBLIC SECTION.    DATA:      message TYPE char50.    METHODS:      constructor        IMPORTING          message TYPE char50 OPTIONAL.
ENDCLASS.                    "LCX_MY_EXCEPTION DEFINITION

CLASS lcx_my_exception IMPLEMENTATION.
  METHOD constructor.    super->constructor( ).    me->message = message.  ENDMETHOD.                    "CONSTRUCTOR
ENDCLASS.                    "LCX_MY_EXCEPTION IMPLEMENTATION

 

I will demonstrate both the classic exception and resumable exception on a division operation with methods DIVIDE and DIVIDE_RESUMABLE

 

CLASS lcl_math IMPLEMENTATION.  METHOD divide_resumable.    TRY.      IF i_denominator = 0.        RAISE RESUMABLE EXCEPTION TYPE lcx_my_exception          EXPORTING            message = 'Divide by zero with the following values:'.      ELSE.        result = i_numerator / i_denominator.      ENDIF.      WRITE:/ 'Numerator:', i_numerator,              'Denominator:', i_denominator.    ENDTRY.  ENDMETHOD.                    "divide_resumable  METHOD divide.    IF i_denominator = 0.      RAISE EXCEPTION TYPE lcx_my_exception        EXPORTING          message = 'Divide by zero with the following values:'.    ELSE.      result = i_numerator / i_denominator.    ENDIF.

* !!! NOT RESUMABLE !!!
* The following line will never be hit when denominator = 0
    WRITE:/ 'Numerator:', i_numerator,            'Denominator:', i_denominator.  ENDMETHOD.                    "divide
ENDCLASS.                    "lcl_math IMPLEMENTATION

 

Note the difference in raising commands:

Method DIVIDE_RESUMABLE

RAISE RESUMABLE EXCEPTION TYPE lcx_my_exception

 

Method DIVIDE

RAISE EXCEPTION TYPE lcx_my_exception

 

Here follows a demo program that calls the division operation in both resumable and non-resumable form:

 

DATA:  gr_ex    TYPE REF TO lcx_my_exception,  g_result TYPE anzh2,  g_err    TYPE abap_bool.

START-OF-SELECTION.
  TRY.      g_result = lcl_math=>divide(        i_numerator = 10        i_denominator = 0      ).

*     !!! NOT RESUMABLE !!!
*     The following CASE block will never be executed when denominator = 0      CASE g_err.        WHEN space.          WRITE:/ 'Result:', g_result.        WHEN OTHERS.          WRITE:/ 'Result undefined'.      ENDCASE.    CATCH lcx_my_exception INTO gr_ex.      g_err = abap_true.      WRITE:/ gr_ex->message.  ENDTRY.  SKIP 1.  CLEAR:    g_err,    gr_ex.  TRY.      g_result = lcl_math=>divide_resumable(        i_numerator = 10        i_denominator = 0      ).      CASE g_err.        WHEN space.          WRITE:/ 'Result:', g_result.        WHEN OTHERS.          WRITE:/ 'Result undefined'.      ENDCASE.    CATCH BEFORE UNWIND lcx_my_exception INTO gr_ex.      WRITE:/ gr_ex->message.      IF gr_ex->is_resumable = abap_true.        g_err = abap_true.        RESUME.      ENDIF.  ENDTRY.

 

 

Note the difference in catch commands:

Method DIVIDE_RESUMABLE (calls RESUME which returns to previous processing right after command which raised this exception)

 

CATCH BEFORE UNWIND lcx_my_exception INTO gr_ex.
...
RESUME

 

Method DIVIDE

 

CATCH lcx_my_exception INTO gr_ex.

 

And the output of this simple from is shown on follwing screenshot

 

 

Output of demo program for Resumable exceptions

 

 

This article has been taken from my blog at oprsteny.com

Have fun with system log - your debugging activity is being recorded by System

$
0
0

Hi developers,

 

I guess most of you have ever changed the variable content in the debugger every day, right?

Suppose you are doing some evil thing ( for example, bypass the authorization check via debugger ) in a system where you have enough authorizations to change

the variable value in debugger, are you aware that your crime is already being recorded and you will be challenged by your system admin based on that evidence?

Sure, they are recorded in system log, SM21.

 

SM21 is very easy to use, just specify the criteria:

 

clipboard1.png

 

Suppose I changed the content of LV to 123.

 

clipboard2.png

 

This is the respective entry recorded in SM21.

( You can get an overall view of what activities you have done on the system during that day. You also observed that once you log on system AG3 via SAP gui,

the dispatcher automatically assign you with a appropriate application server instance. Still remember BC400? )

 

clipboard3.png

 

double click it to navigate to detail. You can deny by saying that "someone stole my user and did that operation". However the Terminal will make your excuse as a nonsense.

 

clipboard4.png

 

Even when you change the process flow in debugger via Shift+F12, it will also be recorded.

clipboardss.png

 

clipboard5.png

 

If you click Trace button, you can get a more detailed technical log based on OS level. By clicking "Display Components" you can set a filtler to only those traces which you are interested.

 

clipboard6.png

 

Those trace files are stored in application server folder DIR_HOME, you can view them via AL11. Double click:

 

clipboard7.png

 

And the dev_XXX files are just what you have seen in SM21.

 

clipboard8.png

 

if you want to operate on those log programmingly, you can have a look at the code in package SYSLOG.

How to Protect Your ABAP Code Against SQL Injection Attacks

$
0
0

SQL injection is one of the most common code injection attack methods, in which malicious SQL statements are inserted to execute unauthorized SQL statements in the database, e.g. read data or modify data in the database. They can be inserted as input by the user through the user interface or by a program through the parameter interface from outside.

 

Two SQL injection variants can be found in ABAP. First is direct usage of user input to create or modify ABAP Open SQL statements to access or modify data in the database (dynamic Open SQL Injection). The other option is when using native SQL commends constructed from user input, for example when using the ABAP Database Connectivity API (ADBC) to execute database statements.

 

Today I will write about Open SQL injection in ABAP. Let’s start with the following example. Do you think this report is safe against SQL injection attacks?

 

REPORT Z_SQL_INJECTION_OPENSQL.
PARAMETERS: street  TYPE zemployees-street    LOWER CASE,
            zipcode
TYPE zemployees-zipcode   LOWER CASE,
            city   
TYPE zemployees-city      LOWER CASE,
            phone  
TYPE zemployees-phone_ext.
DATA: set_expr TYPE string,
      user    
TYPE xubname.
IF street IS NOT INITIAL.
  set_expr
= set_expr && ` STREET    = '` && street && `'`.
ENDIF.
IF zipcode IS NOT INITIAL.
  set_expr
= set_expr && ` ZIPCODE   = '` && zipcode && `'`.
ENDIF
.
IF city IS NOT INITIAL.
  set_expr
= set_expr && ` CITY      = '` && city && `'`.
ENDIF.
IF phone IS NOT INITIAL.
  set_expr
= set_expr && ` PHONE_EXT = `  && phone.
ENDIF.
IF set_expr IS NOT INITIAL.
  user
= cl_abap_syst=>get_user_name( ).
 
UPDATE zemployees
    
SET (set_expr)
  
WHERE userid = user.
 
IF sy-subrc = 0.
   
MESSAGE 'Your address was changed.' TYPE 'I'.
 
ELSE.
   
MESSAGE 'Error when trying to update your address!' TYPE 'E'.
 
ENDIF.ELSE.
 
MESSAGE 'No data given => No Update!' TYPE 'I'.
ENDIF. 

 

This report uses four input parameters: street, ZIP code, city and phone number, from SAP GUI to update the user’s address in the table ZEMPLOYEES. 

 

sql_injection_01.png

After entering the new address information, the user executes the report. The Open SQL statement which is transferred to the database looks like this:

 

 

UPDATE zemployees SET STREET = 'xyz' ZIPCODE = '1234' CITY = 'xyz' PHONE_EXT = 123 WHERE userid = Test.

 

As this is a valid Open SQL statement, the report runs successfully. Now the user’s address is updated.

 

Where is the problem?

 

Assume the table ZEMPLOYEES has following fields:

sql_injection_02.png

As you see, there is a SALARY field. Now this is interesting. Maybe the user wants to update his address like this:

sql_injection_03.png

After the input is given to the report, the Open SQL statement is generated as follows:

UPDATE zemployees SET STREET = 'xyz' SALARY = '3000' ZIPCODE = '1234' CITY = 'xyz' PHONE_EXT = 123 WHERE userid = TEST.

 

As you see, this is also a valid Open SQL statement. Therefore, the report runs successfully with this input. After execution of the report, not only the user’s address is updated, but also the salary of this user. The user is happy about the increased salary he now receives every month.

 

The problem is that this report uses a user’s input as a dynamic SQL change expression (UPDATE zemployees SET (set_expr)WHERE userid = user) without sanitizing or encoding the input provided by the user. As a result, the user can enter his or her own code (xyz' salary = '3000) which modifies the ABAP Open SQL statement and thus enables the user to manipulate data he or she should not be able to.

 

This is one typical Open SQL injection attack. Yes, it could also happen in your ABAP code.

 

SQL Injection in ABAP

 

Most ABAP programs use Open SQL statements to access the central database. The Open SQL syntax allows you to specify every clause of an Open SQL statement as one data object in parentheses to set its value dynamically. As in the example shown above, (set_expr) is used in a SET clause in the UPDATE statement. If such a value is set from outside as part of Open SQL statements without sufficient escaping, it creates a potential SQL injection vulnerability in your ABAP code.

 

The following manipulation attacks of dynamic Open SQL are possible:

 

  • Manipulation of the dynamic WHERE condition
  • Manipulation of the SET clause in the statement UPDATE
  • Manipulation of a dynamic WHERE condition using the parameter I_FILTER of the object services method CREATE_QUERY

 

The following attacks on unauthorized data accesses of dynamic Open SQL are possible:

 

  • Illegal read access to a database table with a SELECT statement
  • Illegal read access to table columns
  • Illegal use of columns in a dynamic GROUP BY clause
  • Illegal use of columns in a dynamic HAVING clause

SQL injection countermeasures for Open SQL

 

The entire problem occurs because the dynamic SQL statement generation uses input from an insecure source, for example from outside the program. Therefore all ABAP developers should be aware of this risk and try to secure their code by strict validation or encoding any input for the Open SQL statement generation.  Use the following two rules against Open SQL injection attacks in your ABAP code:

 

  • Use static Open SQL statements where possible.

Check whether it is really necessary to use dynamic Open SQL or dynamic SQL statements in ADBC (ABAP Database Connectivity API). If not, switching to static SQL statements will eliminate the risk of an SQL injection.

 

  • Always validate or encode input for dynamic statements.

If dynamic SQL statements must be used, then use class CL_ABAP_DYN_PRG to implement input checks and escape the input for the dynamic clauses.

 

Now let’s fix the vulnerability of the program above using the method QUOTE() of the class CL_ABAP_DYN_PRG. The method CL_ABAP_DYN_PRG=>QUOTE() puts single quotes around the input value and escapes single quotes to prevent unwanted Open SQL statements. The phone parameter in the report has type integer and does not need to be escaped. 

REPORT Z_SQL_INJECTION_OPENSQL.
PARAMETERS: street  TYPE zemployees-street    LOWER CASE,
            zipcode
TYPE zemployees-zipcode   LOWER CASE,
            city   
TYPE zemployees-city      LOWER CASE,
            phone  
TYPE zemployees-phone_ext.DATA: set_expr TYPE string,
      user    
TYPE xubname.
IF street IS NOT INITIAL.
  set_expr
= set_expr && ` STREET    = ` && cl_abap_dyn_prg=>quote( street ).
ENDIF.
IF zipcode IS NOT INITIAL.
  set_expr
= set_expr && ` ZIPCODE   = ` && cl_abap_dyn_prg=>quote( zipcode ).
ENDIF.
IF city IS NOT INITIAL.
  set_expr
= set_expr && ` CITY      = ` && cl_abap_dyn_prg=>quote( city ).
ENDIF.
IF phone IS NOT INITIAL.
  set_expr
= set_expr && ` PHONE_EXT = ` && phone.
ENDIF.
IF set_expr IS NOT INITIAL.
  user
= cl_abap_syst=>get_user_name( ).
 
UPDATE zemployees
    
SET (set_expr)
  
WHERE userid = user.
 
IF sy-subrc = 0.
   
MESSAGE 'Your address was changed.' TYPE 'I'.
 
ELSE.
   
MESSAGE 'Error when trying to update your address!' TYPE 'E'.
 
ENDIF.ELSE.
 
MESSAGE 'No data given => No Update!' TYPE 'I'.
ENDIF.

 

If the user puts xyz' salary = '3000 as a new address parameter, the SQL statement which is transferred to the database is as follows:

UPDATE employees SET STREET  = 'xyz'' salary = ''3000' ZIPCODE = '1234' CITY    = 'xyz'  WHERE userid = TEST.

 

This is a valid SQL statement and will execute successfully. But the input xyz' salary = '3000 is encoded and transferred as one complete string 'xyz'' salary = ''3000' for the STREET parameter and put into the SET clause of the ABAP SQL statement.  The ABAP compiler recognizes salary as part of the input for STREET, not as a separate column any more. Therefore a user would be unable to manipulate the column SALARY. As a result, the code is secured against SQL injection attacks.

 

The class CL_ABAP_DYN_PRG offers not only a quote() method, but also many other methods to check and validate the contents of variables. Some of the methods are as follows.

  • check_column_name() checks whether the input is a valid column name.
  • check_table_name_str() checks whether the input is a valid database table name in a specific package.
  • check_whitelist_str() checks the input against  a given whitelist.

 

More information about the class CL_ABAP_DYN_PRG can be found in the class documentation.

 

Last but not least, SAP NetWeaver Application Server, add-on for code vulnerability analysis provides static code analysis and can be used for finding such potential SQL injection positions in ABAP coding.

 

As a responsible ABAP programmer, you pay attention to such security problems. Try to secure your code and create the best code you can.

 

Related Information

 

SAP NetWeaver Application Server, add-on for code vulnerability analysis

(http://scn.sap.com/community/abap/blog/2013/07/31/abap-security-at-teched-2013--scan-analyze-and-fix-your-programs)

Secure Programming – ABAP

(http://help.sap.com/saphelp_nw74/helpdata/en/58/4d767ed850443c891ad27208789f56/content.htm)

SAP note 1520356 – Avoiding SQL Injections

(http://service.sap.com/sap/support/notes/1520356)

ABAP document - SQL Injections

(http://help.sap.com/abapdocu_740/en/index.htm?url=abensql_injections_scrty.htm)


Four different TEST ISOLATION techniques to build your ABAP unit test

$
0
0

Hi friends,

 

As far as I know test isolation is widely used in SAP internal to build unit test code, at least in my team. Test isolation in unit test means that in your production code you make use of some API(class method/Function module) which are developed by other team, and you would not like to really consume them during your unit test, since you would not accept that most red lights in your unit test report are caused by the bugs in those external API,right? Instead, you expect that the call of those external API during your unit test execution will be replaced by some dummy code written by yourself.

I will show you four different ways to achieve that.

 

The example comes from one productive class in my project. For simplicity reasons I don't list unrelevant code here. The class is ZCL_DESTRUCTION_IMG_TOOL_S1.

clipboard1.png

The main logic is doing a cleanup work: loop every work list item, check whether it has some dependent object. If so, delete it.

 

 

 method RUN.    DATA: lv_social_post_id TYPE string.    fill_worklist( ).    LOOP AT mt_worklist INTO lv_social_post_id.       IF dependent_object_existed( lv_social_post_id ) = abap_false.          delete( lv_social_post_id ).       ENDIF.    ENDLOOP.  endmethod.

 

The worklist is just hard-coded:

 
method FILL_WORKLIST.     CLEAR: mt_worklist.     APPEND '20130001' TO mt_worklist.     APPEND '20130002' TO mt_worklist.     APPEND '20130003' TO mt_worklist.  endmethod.

 

The reason why test isolation is used in our unit test is because in method dependent_object_existed, we call an API provided by another team, and we don't want that API to be really executed during our unit test execution. For demonstration reason, I use the following code to simulate the production code.

It means during the unit test on this class, the following code is NOT expected to be executed at all.

 

  method DEPENDENT_OBJECT_EXISTED.     WRITE: / 'Productive code to check dependent object existence for ID: ' , iv_social_post_id.  endmethod.

 

Approach1: test subclass instead.

 

1. Change the visibility of method DEPENDENT_OBJECT_EXISTED from private to protected. The idea is in this approach, we create a sub class ( a local test class) which inherits from ZCL_DESTRUCTION_IMG_TOOL_S1. Since the DEPENDENT_OBJECT_EXISTED is now protected, we have the chance to redefine it in the local test class.

 

clipboard2.png

clipboard3.png

The mock code is simple like below:

 

  METHOD dependent_object_existed.     WRITE: / 'Test mock code to check dependent object existence for ID: ' , iv_social_post_id.  ENDMETHOD.

 

And the code for method start:

  method start.    f_cut = lcl_Destruction_Test=>get_sub_instance( ).    f_cut->run(  ).
endmethod.

In this approach, actually the class being unit test are not lcl_destruction_test, instead of the original class ZCL_DESTRUCTION_IMG_TOOL_S1.

 

clipboard4.png

when execution comes into run method, since the subclass only redefines the very method dependent_object_existed, so the execution of the left methods are still using the code of ZCL_DESTRUCTION_IMG_TOOL_S1. That means all methods of ZCL_DESTRUCTION_IMG_TOOL_S1 except dependent_object_existed will be covered by unit test.

 

clipboard5.png

And when execution comes into method dependent_object_existed,since currently "me" points to the local test sub class, so the mock code will be executed, which is just what we expected.

 

clipboard6.png

The limitation of this approach is, if the author of class ZCL_DESTRUCTION_IMG_TOOL_S1 insists on that it should be defined as final, it is impossible to define any sub class of it.

 

Approach2 - interface extraction + optional argument

The idea is to extract the logic written in method dependent_object_existed and put its implementation into method dependent_object_existed of a new interfaceZIF_SOC_DEPENDENCY_DETECTORinstead. Via such abstraction, the loose coupling of dependency detection call and its implementation is achieved.

 

Necessary change on production classZCL_DESTRUCTION_IMG_TOOL_S2 source code:

1. Private method dependent_object_existed can be removed now.

 

clipboard7.png

2. Create a local class to implement the productive logic of dependency detection as usual:

clipboard8.png

3. Create an optional argument of method run.

clipboard9.png

4. In method run, if no reference is passed in for io_dep_obj_detector, the default one for production use will be called (line5).If there is indeed one dependent object detector passed in, use that one. (line7)

Also the previous method dependent_object_existed is removed; the interface method call is used instead.

 

clipboard10.png

5. The unit test class also implements interface method dependent_object_existed.In unit test code, the optional parameter of method run will be filled, so that the redefined dependency detection logic implemented in interface method in unit test code will be called instead ( according to the logic in step4, line 7)

clipboard11.png

Now the possibility is given for consumers to provide their own dependency determination logic by implementing interface and filling the optional parameter of method run; however this is not what we expected in the beginning.

Signature of method run is changed, which is a non-critical change. (Adding a new optional method parameter is not an incompatible change)

In my opinion it could not be regarded as a 100% test isolation solution.

 

Approach3 - Dynamic detector initialization

 

Interface extraction is also necessary for this solution.

Necessary change on production classZCL_DESTRUCTION_IMG_TOOL_S3’s source code:

1. Create a local class to implement the productive logic of dependency detection just the same as approach2.

2. Add a new static member attribute for technical name of dependency class name. Default value is local class name created in step1:

 

clipboard12.png

3. Initialize the detector instance according to its name maintained in member attribute mv_detector_type_name.

Since its default value is the local class LCL_PROD_DEP_DETECTOR, so in the runtime the productive detection logic will be called.

 

clipboard13.png

4. Define the unit test class as the friend of class ZCL_DESTRUCTION_IMG_TOOL_S3, so that it can alter even the private attribute mv_detector_type_name of ZCL_DESTRUCTION_IMG_TOOL_S3.

 

clipboard14.png

In the unit test code, just add one line( line 62 ). Now the detection logic redefined in unit test class will be called.

 

clipboard15.png

Approach3 is superior compared with approach2 in that no method signature change is required. Class consumer will have no chance to pass their own detection logic into class in a normal way.

 

However, a new class member attribute is introduced, which could be not necessary in the next approach4.

 

Approach4 - detector reference replacement

 

Similar with approach3 to some degree.

Necessary change on production classZCL_DESTRUCTION_IMG_TOOL_S4 source code:

1. Implement the productive dependency detection logic in local class lcl_prod_dep_detector just the same as approach3.

2. Initialize the productive detector instance in CONSTRUCTOR.

 

  method CONSTRUCTOR.    CREATE OBJECT mo_dep_obj_detector TYPE lcl_prod_dep_detector .  endmethod.

 

3. Implement the interface method in test class.

4. Define unit test class as friend of ZCL_DESTRUCTION_IMG_TOOL_S4.

5. In unit test code, replace the instance of dependency detector with unit test class reference itself, so that our own logic would be called in unit test:

 

clipboard16.png

No new attribute or method interface change is required in this solution. This solution should be used for test isolation whenever possible.

You can find the source code of these four approaches from attachment.

A compare tool: Download and analyze the runtime performance result from SAT

$
0
0

I once participated one project which needs to do benchmarking about how much performance gain we can benefit after we move the whole CRM system on top of HANA.

we plan to do it via steps below:

 

1. Run one test report via SAT on both HANA based and non-HANA based system

2. export trace result( xml format ) of HANA system locally, and import it into non-HANA system, and do comparation in SAT.

clipboard1.png

Soon we find our plan is only feasible and could never be put into practice:

1. The size of the trace file generated by SAT is huge. One trace with 1.9 second would lead to a trace file with size = 147MB.

In several of our scenarios we have execution time with 20 seconds, which leads to an xml file with 1GB. When the runtime increses, the SAT export function does not work - an memory consumption runtime exception occurred.

2. We only care about the performance of the Function module CRM_PRODUCT_GETLIST2 which we are responsible for. For some reasons we could not directly call this FM in our test report, but have to use BOL query instead. As a result, lots of executions deep in BOL - Genil callstacks are also traced, which we are not interested at all. This is actually a painpoint of SAT export function - it does not allow you to only export part of trace result which you are really interested with.

3. The compare functionality in SAT does not work for me. I always got the following runtime error. 

 

clipboard2.png

So I developed a tool for our project, which eliminates the pain pointed mentioned above:

1. it could allow the user to only download the part of the trace which they are intrested, which greatly reduces the trace file size. For example, from 147 MB to 84KB.

 

2. I implemented the compare functionality myself.

 

The tool is very easy to use:

 

1. trace your application as usual using SAT.

2. execute report ZHANA_PRODUCT_SAT_TOOL, download your trace file locally as xml file.

Specify the line from which you would to export.

 

For example in your trace result you have call stack like:

 

A

B

C

D

E

F

 

and you specify C, then the tool will export the trace for you which contains C, D, E, F.

 

clipboard3.png

Execute the report and the xml will be saved locally.

clipboard4.png

And the tool will also display the trace result in ALV:

clipboard5.png

3. trace your application once again and save the second trace result file by repeating step1 and step2. Now you have two xml trace files which are ready for comparison.

4. execute report ZHANA_PRODUCT_SAT_COMPARE.Sorry that I didn't change the parameter description here. HN1 means the trace file which you expect the performance is better, and Q2U is that with poorer performance. Green threshold 50 means when the execution time of HN1 is 50% faster than Q2U, the comparison result row will display a green light to inform you. Yellow Threshold = 20 means if HN1 is at least 20% faster than Q2U but still less than 50% faster than Q2U, a yellow light will be displayed. A red light is displayed, if Q2U is faster than HN1 instead.

 

clipboard6.png

The final comparison result looks like below:

 

clipboard7.png

if you double click on a given row, it will automatically show you the exact source code:

 

clipboard8.png

Note:

1. Comparison means the two trace xml files being compared must come from the application traced by SAT with exactly the same input parameter. If each time you trace your application with different parameter, it is possible that the process execution flow would also be different, which makes the comparison meaningless.

2. Since you expect by double clicking on the trace result, it will automatically navigate to the exact source code. Suppose you are executing the compare report in a system C comparing the trace file exported from system A and system B. And system C does not have the class / FM involved in the two trace files. So please always execute the comparison report in either of the system where you perform the SAT trace.

 

This tool consists of a report ZHANA_PRODUCT_SAT_TOOL for downloading SAT trace result, a report ZHANA_PRODUCT_SAT_COMPARE for comparing the downloaded trace xml file, and three utility classes ZCL_CRM_HANA_TOOL, ZCL_CRM_HANA_XML_TOOL and ZCL_CRM_HANA_COMPARE_TOOL. I put their source code into attachment. I think they will give you an example how to access the trace result done by SAT via programming. Feel free to use them to develop more powerful tools by leveraging SAT. Have fun.

WORK AROUND: NOT AUTHORIZED TO A TRANSACTION

$
0
0

Many abap developers may not have authorization to use many of the transactions. E.g.

 

1.GIF

2.GIF

There is a way to use any transaction even though you don't have access. The Function module C14Z_TRANSACTION_CALL can be used.

Steps:

1. Go to transaction se37:

3.GIF

2. Give the FM "C14Z_TRANSACTION_CALL" and click 'Display'

4.GIF

3. Put a debugger at line no 44

5.GIF

 

4. Execute(F8) and give the Tcode that you want to use.

6.GIF

5. Then Execute. It will open a debugger session. double click on SY-SUBRC and see the value equal to 2.

7.GIF

6. Go to edit mode and make the SY-SUBRC value equal to 1.

 

8.GIF

7. Execute and enjoy

9.GIF

 

It is not recommended to use the transaction which you are not authorized to view.

A Generic way to find the place to store SAP GUI settings in your OS Registry

$
0
0

Hi Friends,

 

Rahul Mahajan has written a great document to demonstrate where does the tcode lists in your SAP gui get stored. You may wonder how does he found that place.

I will show you a generic way on how to find the place in the OS registry where stores all the settings related to SAP GUI.

 

The basic idea is to export the registry as a snapshot before SAP GUI setting is changed, let's say before change snapshot. And change some settings or do something via SAP GUI, and export the registry again let's say as a after change snapshot. Compare the two, the difference what you are looking for.

 

You can use File->Export to export the node HKEY_USER and its children:

 

clipboard1.png

Then change some settings in SAP GUI, and export the after change snapshot.

 

clipboard2.png

 

I use the standard tool available in command line FC ( file comparator) to try to get the difference between the two.

 

clipboard3.png

it failed to finish the mission:

 

clipboard4.png

So I choose a more professional tool: Regshot ( You can download it from Google )

 

You can create two snapshots and it can automatically generate the comparison report to you:

 

clipboard5.png

 

clipboard6.png

Detail changes:

 

clipboard7.png

[HKEY_CURRENT_USER\Software\SAP\General\ControlServices\WebSearchEngine]
"WebSearchEngine"="GOOGLE"

 

clipboard8.png

clipboard9.png

clipboard10.png

clipboard11.png

MIGRATE USR03 TABLE

$
0
0

Replace use of USR03 table

 

 

 

SELECT * FROM usr03.

    CLEAR w_usr03.

    MOVE-CORRESPONDING usr03 TO w_usr03.

 

I start with that, and then I have the this requirement

 

"Due usr03 table is considered obsolete, but we can use it already, we have to change this select sentence and replace it for a non-obsolete table, I dont want none reference to usr03."

 

what do I do if the 28 fields of usr03 table are used in a specific task?

 

where do I find that 28 fields now?

 

How do I prepare the information to use it in the new specification?

 

So I start to investigate and I only find some references but the information was dispersed, with the suggestion for using ADRC, ADCP, ADCR , USR02, and so on of options, after several discussions I could try this:

 

I learned I could use the   CALL FUNCTION 'BAPI_USER_GET_DETAIL' and using returned tables with that Call function, I could specify fields that I need to my processes.

 

the substitution was this way:

 

this is the CALL FUNCTION 'BAPI_USER_GET_DETAIL' definition

 

 

  CALL FUNCTION 'BAPI_USER_GET_DETAIL'

    EXPORTING

      USERNAME             =

*     CACHE_RESULTS        = 'X'

*   IMPORTING

*     LOGONDATA            =

*     DEFAULTS             =

*     ADDRESS              =

*     COMPANY              =

*     SNC                  =

*     REF_USER             =

*     ALIAS                =

*     UCLASS               =

*     LASTMODIFIED         =

*     ISLOCKED             =

*     IDENTITY             =

*     ADMINDATA            =

    TABLES

*     PARAMETER            =

*     PROFILES             =

*     ACTIVITYGROUPS       =

      RETURN               =

*     ADDTEL               =

*     ADDFAX               =

*     ADDTTX               =

*     ADDTLX               =

*     ADDSMTP              =

*     ADDRML               =

*     ADDX400              =

*     ADDRFC               =

*     ADDPRT               =

*     ADDSSF               =

*     ADDURI               =

*     ADDPAG               =

*     ADDCOMREM            =

*     PARAMETER1           =

*     GROUPS               =

*     UCLASSSYS            =

*     EXTIDHEAD            =

*     EXTIDPART            =

*     SYSTEMS              =

            .

So I need an username value, I going to use selveral of the return tables listed above

 

First I did the next declarations:

 

DATA:  F_USERNAME    TYPE BAPIBNAME-BAPIBNAME, " this value will be used as call function parameter

                                              

       USRMANDT      TYPE USR21-MANDT,         " usr03 used mandt as a field

       USRBNAME      TYPE USR21-BNAME,         " we need this field as part of 28 fields of usr03

       USRKOSTL      TYPE USR21-KOSTL,         " we need this field as part of 28 fields of usr03

       S_ADDRESS     TYPE BAPIADDR3,           " we need this type of structure to retain importing address values

       S_LOGON       TYPE BAPILOGOND,          " we need this type of structure to retain importing logon values

       S_DEFFAULT    TYPE BAPIDEFAUL,          " we need this type of structure to retain importing deffault values

                                              

       TAB_RETURN    TYPE BAPIRET2 OCCURS 1.   " we need this type of structure to retain table return values

                                              

 

DATA: BEGIN OF WT_BNAME,                       " to manipulate into a usr21 table loop

      F_BNAME TYPE USR21-BNAME,               

      END OF WT_BNAME.

 

DATA: BEGIN OF TAB_TELEFS OCCURS 0.            " to use several values for this bapiadtel structure

      INCLUDE STRUCTURE bapiadtel.            

DATA: END OF TAB_TELEFS.

 

DATA: BEGIN OF TAB_TELTEX OCCURS 0.            " to use several values for this bapiadttx structure

      INCLUDE STRUCTURE bapiadttx.            

DATA: END OF TAB_TELTEX.

 

DATA: BEGIN OF TAB_TELEX OCCURS 0.             " to use several values for this bapiadtlx structure

      INCLUDE STRUCTURE bapiadtlx.            

DATA: END OF TAB_TELEX.

 

SELECT * FROM usr21.                   " we do the select into usr21

MOVE-CORRESPONDING USR21 TO WT_BNAME.

 

F_USERNAME = USR21-BNAME.               " we assign bname to be used into call function 'BAPI_USER_GET_DETAIL'

CALL FUNCTION 'BAPI_USER_GET_DETAIL'

  EXPORTING

    USERNAME = F_USERNAME

  IMPORTING

    DEFAULTS = S_DEFFAULT               " We assign this "work structure" for the imported tables

    ADDRESS  = S_ADDRESS               " We assign this "work structure" for the imported tables

    LOGONDATA = S_LOGON                   " We assign this "work structure" for the imported tables

  TABLES

    RETURN   = TAB_RETURN               " We assign this "work structure" for the imported tables

    ADDTEL   = TAB_TELEFS               " We assign this "work structure" for the imported tables

    ADDTTX   = TAB_TELTEX               " We assign this "work structure" for the imported tables

    ADDTLX   = TAB_TELEX.               " We assign this "work structure" for the imported tables

 

*------------------------- we need declare fields for the values with the data type specific we want to use.

 

data: begin of w_usr21,

USRMANDT TYPE usr21-mandt,

*...,

*...,

*... and so on,

HUSHOR TYPE BAPIADDR3-TIME_ZONE,

end of w_usr21.

 

* total: 28 field declarations

 

*-------------------------after that we asigned the next values located inside structures declared above. ( any name can be declared)

 

W_USR21-USRMANDT = USR21-MANDT.

W_USR21-USRBNAME = USR21-BNAME.

W_USR21-FIRSTNAME = S_ADDRESS-FIRSTNAME.

W_USR21-LASTNAME = S_ADDRESS-LASTNAME.

W_USR21-MIDDLENAME = S_ADDRESS-MIDDLENAME.

W_USR21-SECONDNAME = S_ADDRESS-SECONDNAME.

W_USR21-TITLEP = S_ADDRESS-TITLE_P.

W_USR21-DEPARTMT = S_ADDRESS-DEPARTMENT.

W_USR21-USRKOSTL = USR21-KOSTL.

W_USR21-BUILDING = S_ADDRESS-BUILDING_P.

W_USR21-FLOOR = S_ADDRESS-FLOOR_P.

CONCATENATE S_ADDRESS-STREET S_ADDRESS-ROOM_NO_P INTO W_USR21-S_STRNUM SEPARATED BY SPACE.

W_USR21-APPOSTAL = S_ADDRESS-POSTL_COD2.

W_USR21-CODPOSTAL = S_ADDRESS-POSTL_COD1.

W_USR21-CITY = S_ADDRESS-CITY.

W_USR21-REGION = S_ADDRESS-REGION.

W_USR21-COUNTRY = S_ADDRESS-COUNTRY.

W_USR21-LANG = S_ADDRESS-LANGU.

W_USR21-NTELEF = S_ADDRESS-TEL1_NUMBR.

W_USR21-TELNR = '--'.     " there is no value for that field

W_USR21-TEL01 = '--'.     " there is no value for that field

W_USR21-TEL02 = '--'.     " there is no value for that field

W_USR21-NTELEX = TAB_TELEX-TELEX_NO.

W_USR21-NFAX = S_ADDRESS-FAX_NUMBER.

W_USR21-NTELTEX = TAB_TELTEX-TELETEX.

W_USR21-POBOXCIT = S_ADDRESS-PO_BOX_CIT.

W_USR21-POBOXBARR = S_ADDRESS-PBOXCIT_NO.

W_USR21-HUSHOR = S_ADDRESS-TIME_ZONE.

 

*- so we have 28 assigned values.

 

*-------------------------then we do several operations with the fields

do.

 

*---

*----

*---

*---

 

*------------------------- and finally.

 

ENDSELECT.

 

 

*------------

*- Then we can migrate or forget of the use of usr03.

*- I´am working to find out and solve the 3 fields phone problem, because I did'nt find this fields,but this code help me with the problem.

Viewing all 948 articles
Browse latest View live


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