Quantcast
Channel: SCN : Blog List - ABAP Development
Viewing all 943 articles
Browse latest View live

LUW - Part1

$
0
0

Logical unit of work (LUW) in SAP:

The objective of this document is aimed at explaining the concept of logical unit of work in SAP. I assume that this will be a good read for my fellow readers.

 

Why logical units of work?

The main purpose of the logical unit of work is to ensure data consistency in the R/3 systems. Let us consider that there is a complex R/3 transaction that involves a series of dialog steps; during the execution of one such dialog step at the middle of the transaction the data involved will lack completeness & will therefore be inconsistent. When talking about of logical unit of work we must note the following points initially.

  1. Database LUW also known as DB LUW, each dialog step has its own DB LUW that is used to ensure data consistency.
  2. SAP LUW consists of several dialog steps and all the changes to database are written in a single DB LUW. We must note that the database changes are always written in the final database LUW.
  3. Therefore to summarize a complex R/3 transaction may span across several dialog steps each having its own DB LUW, however all the changes are written to the database in the final DB LUW of the SAP LUW after a commit work is executed by the system or rolled back with no changes written to the database due to errors.


What is a database LUW?

A database LUW is work unit consisting of database operations (INSERT, UPDATE, MODIFY & DELETE) that it used to make the database changes.  The final database LUW of a SAP LUW is used to make the changes to the database, the changes to the database is not made until after the database commit. The database LUW is executed in a special kind of work process called the update work process.


What is a SAP LUW?

A SAP LUW is a logical unit work that spans over several dialog steps. Let us consider a complex R/3 transaction of n screens; where each screen has its own DB LUW involving database operations and are logically related to each other. It is important that the changes to the database for this complex transaction must be made all at once or it must be rolled back all for the data consistency; that is where the SAP LUW becomes critical in maintaining a logical relationship between several DB LUWs (dialog phases) & making all the changes to the database in the final DB LUW. SAP LUW uses the bundling technique to achieve the same. There are several possibilities of bundling technique and one of them is bundling the database changes using a function module call in the UPDATE TASK.

 

Bundling in SAP LUW:

A CALL FUNCTION…IN UPDATE TASK is one of the bundling techniques to achieve all the database changes in the last DB LUW of the SAP LUW. The open SQL statements for database changes are placed in the function module instead of placing them in the code, using the addition UPDATE TASK ensures that the function module is not executed until COMMIT WORK is found in the program. At COMMIT WORK the function module calls with UPDATE TASK are executed in a special work process called update work process and the changes to the database is executed in the final DB LUW of the SAP LUW.


R/3 transactions using update function modules.

Let us consider there is one R/3 transaction calling another R/3 transaction and as well as calling some update function module. There is some typical behavior that can be noted for this particular scenario.

  • (a)    The called transaction begins in a new SAP LUW in comparison to the calling transaction. It implies that there are two different SAP LUWs now.
  • (b)   A new update key is generated for the new SAP LUW that is used to identify all the update function modules.
  • (c)    If the called R/3 transaction does not have a COMMIT WORK then the update function modules will not be executed i.e. both the calling transaction & the called transaction must have their own COMMIT WORK.
  • (d)   The update function modules are called only after the system has executed a COMMIT WORK.
  • (e)   Let us consider there is an importing parameter -IM_X  for the update function module; now the same function module is called at several places in the program like shown below –

DATA: gv_x TYPE i.

gv_x = 1.

CALL FUNCTION UPD_FUNC IN UPDATE TASK

EXPORTING

         IM_X = gv_x.

gv_x = 2.

CALL FUNCTION UPD_FUNC IN UPDATE TASK

EXPORTING

         IM_X = gv_x.

 

gv_x = 3.

COMMIT WORK.

 

What will be the value of gv_x at the COMMIT WORK?

No, the value of gv_x will not be 3; when commit is executed by the system the value of gv_x that will be passed is dependent at the point of function call therefore during the first call the value of gv_x will be 1 & during the second call the value of gv_x will be 2.


ABAP Dependency Injection - An implementation approach

$
0
0

Introduction

In this blog I want to show you some concepts of a solution for a "Dependency Injection"(DI)-approach which is used in one of my recent projects. It consists of a DI-Framework written in ABAP and an integration-pattern to get the framework working in a large component-based ABAP-application. The DI-framework is custom-made to fit the needs of the chosen design-approach. I will focus on the parts which helps you to organize your DI-container in a component based software design.

 

I won't give you an introduction to DI but rather explain an implementation approach to fit into the abap world. If you're looking to understand the basics of DI you should read "The issue with having many small classes" and "Dependency Injection for ABAP" or even the wikipedia page for DI would be a good start.

 

Getting to the requirements

In my recent project I was looking for a flexible solution of managing my OO-instances that build up my applications. While a factory pattern only describes how to design a factory for access by a client, it doesn't describe how the factory should internally manage instances that it produces. Most of the time you will put a lot of "create object" statements into your factory to set up your application. By this your application-"set up" will be fixed.

Also if you follow the SOLID-Principals in your software-design you will soon face problems described in the blog "The issue with having many small classes". You have a lot of small classes and you have to write code for instance-creation and management in your factory-classes. This could become annoying.

Another drawback is that it's hard to reuse your factory since your application-"set up" is fixed and you can't replace certain classes for special purposes.

 

So the solution is to combine the factory pattern with a DI-framework. The DI-framework handles the instance management and the factory-pattern provides a way to access certain instances. The benfit is, that you keep the framework-code inside the factory and out of your business code. Someone who uses your factory doesn't even have to know about the DI-framework.

 

So in the end the framework fulfills the following reqirements

  • Quick/easy to set up
  • Debugging possibilities
  • Support for all DDIC-Datatypes
  • No dependencies in business code (i.e. not forcing to inherit some class)
  • Class creation (with "create object" or factory methods)
  • Class configuration (constructor-based or with setter-methods)
  • Modularization to match a component based architecture
  • Support for "where-used list"
  • Replacing classes in a given setup

 

Quick Example

I will first start to give you a quick introduction into the DI-framework and how the basic usage is.

The following class-diagram shows the setup for the examples ("zif_*" for interfaces; "zcl_*" for classes).

Klassenmodell.gif

 

"zcl_service_a" needs some other classes to get its job done. "zcl_persistence_a" implements some kind of database access and "zcl_calulate_a" provides some kind of calculation.

If you want to use "zif_service_a" you first have to instantiate "Persistence A" and "Calculate A". Then you can create "Service A".

The ABAP code would look like this (In ABAP NW740 syntax)

  DATA(lo_persistence) = NEW lcl_persistence_a( ).  DATA(lo_calculate) = NEW lcl_calculate_a( ).  DATA(lo_service) = NEW lcl_service_a(                                        io_pers = lo_persistence                                        io_calc = lo_calculate ).

This is quite simple. You could put that into your factory and you would be fine. But if your component grows and involves more and more classes the task of putting them together gets annoying. Especially if you tend to create small classes.

On the other hand it gets difficult to reuse your setup (the way you put your classes together). If you want to use your service in a slightly different context and want to replace i.e. the calculate-implementation by your own you have to copy the whole factory and replace the line where you create the calculate-instance. Some nicer approach would be to say: "do everything as is, but instead of your calculate implementation take mine".

 

With the DI framework you would do something like this. In the first step you would create the DI-container:

  " Create DI-Container  DATA(lo_container) = zcl_di_container=>create_instance_default( ).

Then you would register all your classes in the container, so the container has a bulk of classes to work on.

  " Register classes into container  lo_container->register_classname( iv_classname = 'ZCL_SERVICE_A' ).  lo_container->register_classname( iv_classname = 'ZCL_PERSISTENCE_A' ).  lo_container->register_classname( iv_classname = 'ZCL_CALCULATE_A' ).

Now you can query the container to get an instance of your "Service A":

  DATA lo_service TYPE REF TO zif_service_a.  lo_container->get_instance_value(    CHANGING cv_target_value = lo_service ).
" Use your instance
lo_service->do_something( ... ).

The container analyzes the type of the variable "lo_service". Since it is "zif_service_a" the container looks for a registered class that matches this interface. This would be "zcl_service_a". It analyzes the constructor of this class and looks for classes that match the type of the specific parameters. So it will first create instances of "zcl_persistence_a" and "zcl_calculate_a" before it creates an instance of "zcl_service_a". The instance will be copied into the variable "lo_service".

The construction and analyzing process is done recursively and it can detect possible inifinite recursions. You can even use other datatypes on your parameters like int, string, structure or tables, which also have to be registered in the container.

 

Replacing classes

The code above would be placed into some kind of factory. While the registration code would be placed into the constructor, the querying code would be placed into the factory methods.

CLASS zcl_factory_a IMPLEMENTATION.  METHOD constructor.    ao_container = zcl_di_container=>create_instance_default( ).    ao_container->register_classname( iv_classname = 'ZCL_SERVICE_A' ).    ao_container->register_classname( iv_classname = 'ZCL_PERSISTENCE_A' ).    ao_container->register_classname( iv_classname = 'ZCL_CALCULATE_A' ).  ENDMETHOD.  METHOD get_service_a.    " ** RETURNING VALUE(ro_service_a) TYPE REF TO zif_service_a.    ao_container->get_instance_value( CHANGING cv_target_value = ro_service_a ).  ENDMETHOD.
ENDCLASS.

If you want to replace a specific class you can do it by inheriting the factory-class and register your own implementation. I.e. if you want to replace the implementation "ZCL_SERVICE_A" of interface "ZIF_SERVICE_A" by your own implementation "ZCL_SERVICE_A_BETTER" you can do this:

CLASS lcl_factory_a_better IMPLEMENTATION. " INHERITING FROM zcl_factory_a  METHOD constructor.    super->constructor( ).    ao_container->register_classname( i_var_classname = 'ZCL_SERVICE_A_BETTER' ).  ENDMETHOD.
ENDCLASS.

The container acts like a stack and every new registered datatype or class would be put on top of this stack. If you query for a specific type the container checks for matches from top to bottom. The first match will be returned and used. Since your own class ZCL_SERVICE_A_BETTER is registered last it is on top of the stack. So it will be the first matching type for the interface "zif_service_a" and will be selected. By this way you can replace specific classes.

 

Debugging

If your container contains a large amount of classes you may want to check if instances are bound to the correct parameters. The di-framework provides a possibility to trace the querying of the container. The trace is rendered into a graph. The following picture shows the graph from the small example above:

 

ServiceA.gif

 

You can see that both parameters of the constructor from "zcl_service_a" are bound to the correct classes.

 

Namespaces

Another concept realized in this DI-framework are namespaces. In an application that consists of different components each component would need its own container. That's because you can't put every class of every component into a single container. This would result in conflicts between different components which implement shared interfaces.

Another option is to use namespaces. Each component uses the same container-data-core but uses a different namespace within this container. The namespaces are isolated from each other so that conflicts are avoided. If you want to use a class of another namespace you can set an alias to the other namespace.

The following example shows the concept of namespaces and aliases.The following class-diagram shows "Service B":

 

KlassenmodellB.gif

 

You can see that "Service B" uses "Service A". Furthermore it implements "zif_calculate" which is also implemented by "Service A". The code to set up the DI-container for this scenario would be the following:

 

  DATA(lo_ctx_data) = NEW zcl_di_context_data( ).

The instance of "lo_ctx_data" is the core of each container. It contains the registered classes and datatypes. If you have different container-instances which share this instance of "lo_ctx_data"  they will all act on the same container data.

If you create a container you can set up a default namespace. If you register classes they will all be placed into the containers default namespace. "Service A" would be set up in the following way:

 

  DATA(lo_container_a) = /abk/cl_di_container=>create_instance_default(                                       i_var_namespace = 'urn:a'                                       i_obj_context_data = lo_ctx_data ).  " Register classes into container  lo_container_a->register_classname( iv_classname = 'ZCL_SERVICE_A' ).  lo_container_a->register_classname( iv_classname = 'ZCL_PERSISTENCE_A' ).  lo_container_a->register_classname( iv_classname = 'ZCL_CALCULATE_A' ).

 

"Service B" is set up in the following way. It will reuse the previously created instance  of "lo_ctx_data":

  DATA(lo_container_b) = /abk/cl_di_container=>create_instance_default(                                       i_var_namespace = 'urn:b'                                       i_obj_context_data = lo_ctx_data ).  " Register classes into container  lo_container_b->register_classname( iv_classname = 'ZCL_SERVICE_B' ).  lo_container_b->register_classname( iv_classname = 'ZCL_PERSISTENCE_B' ).  lo_container_b->register_classname( iv_classname = 'ZCL_CALCULATE_B' ).  lo_container_b->register_alias(                                  iv_typename = 'ZIF_SERVICE_A'                                  iv_query_namespace = 'urn:a' ).

If you retrieve "Service B" ...

  DATA lo_service TYPE REF TO zif_service_b.  lo_container_b->get_instance_value( CHANGING cv_target_value = lo_service ).

... you will get an instance constructed like in the following picture:

 

ServiceB.gif

(Click for a larger image)

 

You can see two namespaces. "urn:a" for "Service A" and "urn:b" for "Service B". "Service B" has a registered "alias" which connects the two namespaces on the type "zif_service_a". So just the type "zif_service_a" of namespace "urn:a" is visible in namespace "urn:b". So you can see that the namespaces are clearly isolated to each other and the querying and construction is restricted to its namespace unless you set an explicit alias.

 

DI-Modules

The last concept I want to show are modules. Modules are a descriptive way to set up a container. You can describe five different aspects with a module:

  1. Module-Name
  2. Default-Namespace
  3. Import dependent modules
  4. Public module items
  5. Container registration

 

The following code shows an example for a module for "Service A":

 

CLASS zcl_module_a IMPLEMENTATION.  METHOD zif_di_factory_module~get_modulename.    rv_module_name = 'Service A'.  ENDMETHOD.  METHOD zif_di_factory_module~get_default_namespace.    rv_namespace = 'urn:a'.  ENDMETHOD.  METHOD zif_di_factory_module~register_imports.    " nothing  ENDMETHOD.  METHOD zif_di_factory_module~register_interface.    io_container_mng->register_alias(      iv_typename = 'zif_service_a'      iv_query_namespace = 'urn:a' ).  ENDMETHOD.  METHOD zif_di_factory_module~register_classes.    io_container_mng->register_classname( iv_classname = 'ZCL_SERVICE_A' ).    io_container_mng->register_classname( iv_classname = 'ZCL_PERSISTENCE_A' ).    io_container_mng->register_classname( iv_classname = 'ZCL_CALCULATE_A' ).  ENDMETHOD.
ENDCLASS.

Modules can hold several namespaces. But there is always one namespace that acts as default namespace. Every class or datatype is registered with the default-namespace unless a namespace is explicitly provided on registration.

 

A module can build a relationship to another module in order to get access to public services (registered classes) provided by the other module. This is done in the "register_import"-Method. I.e. in the previous section "Service B" imports a service from "Service A". In terms of modules this would be setup by a relationship between these modules. So "Service B" would import module "Service A". In the module declaration of "Service B" you would find this method implementation:

  METHOD zif_di_factory_module~register_imports.    io_registry_imports->register_factory_module( NEW zcl_module_a( ) ).  ENDMETHOD.

The method "register_interface" describes which registered types of the module should be public and imported into another namespace once the module is imported elsewhere. The method receives the DI-container of the foreign module and can register aliases into the foreign container. The aliases should point to the modules own namespace. By this namespaces of two modules get connected and dedicated registered types are accessible in the foreign container. So this method describes some sort of public interface of the module.

 

The final method "register_classes" fills up the own default-namespace with classes and datatypes. Every class and datatype has to be registered there, which should be considered on construction of instances by the DI-framework for this module. Into this method you can also place factory classes which are not DI based. So you don't have to use the feature of importing modules to get access to instances of another components.

 

If you want to use a module you can do it with this line:

  DATA(lo_container) = zcl_di_factory_generic=>get_instance( io_init_module = NEW zcl_module_b( ) )->get_di_container( ).

Now you can retrieve your instance like shown in the previous examples:

  DATA lo_service TYPE REF TO zif_service_b.  lo_container->get_instance_value( CHANGING cv_target_value = lo_service ).

The benefits of using modules are that ...

  • you have a managed way how different components are connected to each other.
  • you don't have to care about creating and managing the DI-container.
  • every module is only imported once. So if n modules import the same module its content is not registered n times but just once.
  • since the module-class is used in a descriptive way, you can dynamicly replace modules.

 

Finally the DI-implementation provides a code generator which makes module creation a bit easier. So there is a configuration for the SAP "Service Implementation Workbench" (SIW) that can create the module-code and wraps it into a nice factory-class to provide a type-safe access to the DI-container and hiding the DI aspects from users. The SIW generates your factory and module-code and you just have to fill the five module-methods. In the end you have a nice working DI-based factory-class.

 

Finally

This blog could not handle every aspect of this DI-framework. But I hope I could show you the intended design approach to fit in a large component based application.

Please let me know what you think!

 

Edit (6.1.2013): Some parts in section DI-Modules

Performance analysis of long running programs in ABAP

$
0
0

Performance analysis of long running programs in ABAP:

 

The objective of this blog is to show how to analyze long running programs in ABAP. Customers often complain about some custom reports in ABAP are taking a hell lot of time during the production execution and as a consequence they frequently request to look into the performance killers and the bottlenecks.

Recently, we had a severe performance issue with one such reports in ABAP – a custom report for purchasing contracts. Customer anticipated that the report is running very slow and hence it could not be moved to the production environment. Our team of developers had the job to optimize it. They used the R/3 transaction SAT to understand the bottlenecks and the performance killers.

 

Starting with SAT:

 

We have the following report that runs for a long time. The screen-shot of the report is given below

 

1.png

 

Now we will analyze the performance of this report using the transaction code SAT. Let us run this R/3 transaction, the initial screen of SAT is displayed. Here we are creating a new variant called PERFO_VAR that will contain all the restrictions for the performance measurement.

 

2.png

 

The variant will have the following characteristics to measure the performance –

1.     Choose Per Call Position radio-button – This implies that it enables call per aggregation. This option is generally used for performance trace.

 

3.png

  The Statements tab will look like the screen-shot below:

 

4.png

 

After the creation of the variant; we will start the measurement of the transaction. Please see the screen-shot.

 

5.png

 

Once the measurement is finished; we will go to the evaluate tab to see the results & analyze. W.r.t performance analysis it is best to use the tools – Hit List & Profile to analyze the performance killers.

 

 

6.png

 

 

7.png

 

 

If we look closely at the Hit List ALV, we can see which entries took most of the time. The entry 1 consumed 79.65% of the net time. The entry 2 consumed 15.93% of the net time.  The entry 3 consumed 4.21% of the net time. Hence the first 3 entries sum up to 99.79% of the net time. We will have a detailed look at these 3 entries to understand why they took so much time. One important thing to note at this point in time is that we are mostly interested in the net time; the net time is the time spent on the specific modularization unit; whereas gross time is the total time spent on the lower level modularization units.

Example:

Perform f_send_files

   -> the net time = 0.05%; time spent spent to call the subroutine (considering this specific modularization unit only).

   -> the Gross time = 95.64;   time spent to call all the related lower level modularization units.

 

Thanks to Thomas Zloch for correcting...:)

 

 

Detailed Analysis with SAT:

 

Now in the Hit List, when we double click on the 1st entry; we navigate to the code & oops this what we see –

 

1.png

 

 

Now in the Hit List, when we double click on the 2nd entry; we navigate to the code & oops this what we see –

 

2.png

 

Now in the Hit List, when we double click on the 3rd entry; we navigate to the code & oops this what we see –

 

 

3.png

 

Therefore it is clear that WAIT/SLEEP& WAIT for RFC takes the most of the time in report. They are the major performance killers.

 

Conclusion:

 

SAT is a very useful transaction to do the following things –

1.       Performance Analysis.

2.       ABAP Trace Analysis

3.       Trace Parallel process

4.       User Trace.

It is an extension of commonly used SE30. It is more sophisticated tool & very useful for analyzing production performance issues quickly & neatly.

What is ANST....and why aren't you using it?

$
0
0

ANST – also known as the Automated Notes Search Tool, is a powerful application that searches SAP notes for a specific problem based on the issue in your system.

 

 

The tool is available if you have upgraded your system to one of the following SAP basis support packages (seeKnowledge Base Article 1818192):

 

 

Software Component Release Package name

  ________________________________________________________________________

SAP Basis component

 

 

SAP_BASIS 700 SAPKB70028

 

 

SAP_BASIS 701 SAPKB70113

 

 

SAP_BASIS 702 SAPKB70213

 

 

SAP_BASIS 731 SAPKB73106

 

 

The transaction to start the tool is named ANST_SEARCH_TOOL.

 

 

NEW! By following the instructions in note 1915529, you can utilizethe shorter transaction ANST.  The functionality is exactly the same as ANST_SEARCH_TOOL, but the shorter transaction name makes it more convenient.

 

 

 

 

 

We have been sharing the news about this great tool through multiple channels.

 

 

 

 

 

SCN Blogs

 

 

Our Newest blog shows how ANST can help you solve billing problems.  A special thanks to Tobias Dolgener for sharing.

 

http://scn.sap.com/community/erp/sd/billing/blog/2013/12/06/the-power-of-tools--how-anst-can-help-you-to-solve-problems-yourself

 

 

Have an idea for new features or functionalities? Drop a comment in the following blog!

 

http://scn.sap.com/community/software-support-and-maintenance/blog/2013/09/10/the-future-of-sap-automated-note-search-tool

 

 

 

 

 

Demos/Training

 

 

We have a training system set up for you to learn how to use the tool. 

Access with S-user ID here

 

 

 

 

 

Videos

 

 

Here’s an ECC example, where a note to solve a problem is found in less than 2 minutes

 

 

 

 

 

 

User Groups/Events

 

 

Most recently, we have been presenting to various global user groups to help spread the word about the Automated Notes Search Tool.

 

 

 

 

 

Here are just some of the locations that we have presented. (Photos added if available)

 

 

Christoph Haberl presented ANST to DSAG Vienna.

 

 

Arvin Chiu presented at  the ASUG British Columbia chapter meeting

ANST arvin.png

 

 

 

 

Miguel Alfaro  and Lloyd Goveia presented at the ASUG Ontario chapter meeting.

 

ANSTmiguelandlloyd.png

Mark Richardson blogged about it on SCN.  Thanks Mark!

 

 

 

Felipe Fonseca and Tomas Black presented at the Grand Opening event in Sao Leopaldo, Brazil

ANSTfelipetomas.png

 

 

Carlos Martinez Escribano  presented to the AUSAPE (Spain SAP User Group)

 

 

Preetpal Singh presented at SAP TechEd in Bangalore. 

 

 

 

 

 

 

 

 

Enterprise Support Academy

 

 

For those of you interested in a more personal approach, we have Meet the Expert sessions on the Automated Notes Search Tool scheduled through the SAP Enterprise Support Academy.  You can register for a session (s-user ID login required) here

 

 

Upon entering the site, type in Automated in the search box and you will find the session information.

ANST ESA.png

 

 

 

 

Here is the current schedule:

 

 
22.01.2014 16:00 - 17:00 CET (CET) EMEA English

 

 


22.01.2014 10:00 - 11:00 CET (CET) EMEA German   

 

 

 

 

 

Select the one that’s best for you and register to learn more.  These Meet the Expert sessions are all recorded, so if you can't make the date, look in the replay library!

ANST MTE.png

 

 

 

 

Social Media

 

SAP Product Support is on Twitter and Facebook! 

Stay connected with us via our 2 Twitter channels:

http://twitter.com/SAPSupportCE and http://twitter.com/SAP_Gsupport

 

Like us on Facebook at http://facebook.com/sapproductsupport

 

 

Customers Spreading the Word

 

Nicolas Busson  started using the tool and has been kind enough to share his thoughts on it with the community on SCN.  Thanks Nicolas!

 

 

In the Business Trends 2013 wrap up, Somnath Manna  shares how the ANST is a hidden gem for any SAP functional consultant.  Very true!

 

 

 

Recap

There is a plethora of information available to help you start using the Automated Notes Search Tool, and SAP Product Support has experts that can help you along the way. 

 

 

Are you using ANST? Add a comment below and tell us about your experience with it.

 

 

If not, what’s stopping you?  We want to know!

Classical way to ABAP OO style of coding

$
0
0

Object Oriented ABAP is taking slow phase in adoption for Pure ABAPers(Not working in Webdynpro or other object oriented space); even I took a yearlong to completely do my work in OO.

 

Even I was in a situation when one of my clients questioned, why you code in OO as he was not able to understand. I was dumbstruck and the thought provoked me to write this for guys who are in dilemma on how to move to OO.

 

I have seen many blogs on ABAP OO which will be a good start for learning (explaining concepts and examples) but still faced few challenges in moving to ABAP OO like where and how to start, just putting down my thoughts about the problems which faced little longer and ways I overcame

Few road blocks in using ABAP OO

o    Understanding concepts of OO who has no previous knowledge on OO

o    How to implement the same in our regular work in RICEF objects.

o    How to use major advantages of OO

 

This blog is for experienced developers, who are familiar with OO concepts but lazy in implementing the same in ABAP. Before I talk about any of these stuff, I would like to tell how to get familiarize with classes. (Note: This blog only provides approach and not any examples)

For newbies to ABAP OO, can get familiarize with these links?

 

http://help.sap.com/saphelp_nw2004s/helpdata/en/c3/225b5654f411d194a60000e8353423/frameset.htm

http://wiki.scn.sap.com/wiki/display/ABAP/ABAP+Objects+Getting+Started

 

I will show a small example report getting some entries from MARA and using CL_SALV_TABLE to display the same in ALV in four ways here

·         Traditional way of writing

·         OO way (only using Static methods) – To get the hang of class and method concept

·         OO way (Only Instance methods) – To get the hang of class and method concept

·         My IDEAL way in OO

·         New Trend of and completely moving your report to OO.

 

Traditional way of writing the code:

 

REPORT  ysdnblog_classic.
PARAMETERS : p_rows TYPE count DEFAULT '100'.
START-OF-SELECTION.
  DATA : it_mara TYPE STANDARD TABLE OF mara.
  PERFORM get_data CHANGING it_mara.
  PERFORM display USING it_mara.
*&---------------------------------------------------------------------*
*&      Form  GET_DATA
*&---------------------------------------------------------------------*
FORM get_data  CHANGING ch_mara TYPE mara_tt.
  SELECT * FROM mara INTO TABLE ch_mara  UP TO p_rows ROWS .
ENDFORM.                    " GET_DATA
*&---------------------------------------------------------------------*
*&      Form  DISPLAY
*&---------------------------------------------------------------------*
FORM display  USING    i_mara TYPE mara_tt.
  DATA : lr_table TYPE REF TO cl_salv_table.
  cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
                           CHANGING     t_table        =   i_mara )    .
  lr_table->display( ).
ENDFORM.                    " DISPLAY

OO way (only using Static methods) – To get the hang of class and method concept

 

Let’s start with classes and don’t go in to boring part of explaining what is a STATIC or INSTANCE method (Please google or go through about this stuff). Major developers who are new to OO, first question is whether my method should be INSTANCE or STATIC? Let’s save this question to the last.

First to get the hang of the OO ABAP from traditional ABAP using STATIC methods only and above report looks like this: (suggest continuing writing couple of reports/objects)

 

REPORT  ysdnblog_class_static.
PARAMETERS : p_rows TYPE count DEFAULT '100'.
*----------------------------------------------------------------------*
*       CLASS lcl_main DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS : get_data  ,
                    display.
  PRIVATE SECTION.
    CLASS-DATA it_mara TYPE mara_tt.
ENDCLASS.                    "lcl_main DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_main IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_main IMPLEMENTATION.
  METHOD get_data.
    SELECT * FROM mara INTO TABLE lcl_main=>it_mara  UP TO p_rows ROWS .
  ENDMETHOD.                    "GET_DATA
  METHOD display.
    DATA : lr_table TYPE REF TO cl_salv_table.
    cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
                             CHANGING     t_table        =   lcl_main=>it_mara  )    .
    lr_table->display( ).
  ENDMETHOD.                    "display
ENDCLASS.                    "lcl_main IMPLEMENTATION

START-OF-SELECTION.

  lcl_main=>get_data( ).

  lcl_main=>display( ).

 

OK... I hope by now you got hang of what a traditional report looks in CLASS/METHODS.

 

 

OO way (Only Instance methods) – To get the hang of class and method concept

 

What’s next? Let’s see the same program with instance methods. Additional steps would be declaration of an object and instantiate it to use in your program.

 

REPORT  ysdnblog_class_instance.
PARAMETERS : p_rows TYPE count DEFAULT '100'.
*----------------------------------------------------------------------*
*       CLASS lcl_main DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    METHODS :   get_data  ,
                display.
  PRIVATE SECTION.
    DATA it_mara TYPE mara_tt.
ENDCLASS.                    "lcl_main DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_main IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_main IMPLEMENTATION.
  METHOD get_data.
    SELECT * FROM mara INTO TABLE me->it_mara  UP TO P_rows ROWS .
  ENDMETHOD.                    "GET_DATA
  METHOD display.
    DATA : lr_table TYPE REF TO cl_salv_table.
    cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
                             CHANGING     t_table        =   me->it_mara  )    .
    lr_table->display( ).
  ENDMETHOD.                    "display
ENDCLASS.                    "lcl_main IMPLEMENTATION

START-OF-SELECTION.

data : lr_main TYPE REF TO lcl_main.

create OBJECT lr_main.
  lr_main->get_data( ).
  lr_main->display( ).

 

In the above example we declare an object reference of type LCL_MAIN and have to command CREATE OBJECT to create a reference for further usage of the same in program. (The same LCL_MAIN can be declared with different names (many references) and initiated based on the requirement needs)

Please do some live programs either using with any of the above ways to really get initial kick start of OO.

 

My IDEAL way in OO

MY IDEAL way of writing the above program would be as below.


REPORT  ysdnblog_class_ideal.
parameters : p_rows type count default '100'.
*----------------------------------------------------------------------*
*       CLASS lcl_main DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS : start.
  PRIVATE SECTION.
    METHODS : get_data  ,
              display.
    CLASS-DATA : lr_main TYPE REF TO lcl_main.
    DATA it_mara TYPE mara_tt.
ENDCLASS.                    "lcl_main DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_main IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_main IMPLEMENTATION.
  METHOD start.
    CREATE OBJECT lr_main.
    lr_main->get_data( ).
    lr_main->display( ).
  ENDMETHOD.                    "start
  METHOD get_data.
    SELECT * FROM mara INTO TABLE me->it_mara  UP TO P_rows ROWS .
  ENDMETHOD.                    "GET_DATA
  METHOD display.
    DATA : lr_table TYPE REF TO cl_salv_table.
    cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
    CHANGING     t_table        =   me->it_mara  )    .
    lr_table->display( ).
  ENDMETHOD.                    "display
ENDCLASS.                    "lcl_main IMPLEMENTATION

 

START-OF-SELECTION.

 

  lcl_main=>start( ).

 

 

 

Here we call the START method only once for a program and so I made it as a static method and one static object (LR_MAIN referencing the same class ) for dealing with rest of the business logic.  (There can be many better ways as well.. )

New Trend of and completely moving your report to OO:

 

The new way of writing the reports includes t-code to launch your report. Let’s start with T-code creation as below and select 3rd option METHOD OF A CLASS (OO TRANSACTION).

 

 

Next step navigates to below screen and un check the box OO TRANSACTION MODEL enabling another field LOCAL IN PROGRAM

 

 

 

Now provide your program name and local class name and method for the below program. Program looks like below


REPORT  ysdnblog_class_new.

 


SELECTION-SCREEN : BEGIN OF SCREEN 200.
PARAMETERS p_rows TYPE count DEFAULT '100'.
SELECTION-SCREEN : END OF SCREEN 200.

 

*----------------------------------------------------------------------*
*       CLASS lcl_main DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS : start.

 

  PRIVATE SECTION.
    METHODS : get_data  ,
              display.
    CLASS-DATA : lr_main TYPE REF TO lcl_main.
    DATA it_mara TYPE mara_tt.
ENDCLASS.                    "lcl_main DEFINITION
*----------------------------------------------------------------------*
*       CLASS lcl_main IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_main IMPLEMENTATION.
  METHOD start.
BREAK-POINT.
    CALL SELECTION-SCREEN 200.
    IF sy-subrc IS INITIAL.

 

      CREATE OBJECT lr_main.
      lr_main->get_data( ).
      lr_main->display( ).
    ENDIF.

 

  ENDMETHOD.                    "start
  METHOD get_data.
    SELECT * FROM mara INTO TABLE me->it_mara  UP TO p_rows ROWS .
  ENDMETHOD.                    "GET_DATA
  METHOD display.
    DATA : lr_table TYPE REF TO cl_salv_table.
    cl_salv_table=>factoryIMPORTING    r_salv_table   = lr_table
    CHANGING     t_table        =   me->it_mara  )    .
    lr_table->display( ).
  ENDMETHOD.                    "display
ENDCLASS.                    "lcl_main IMPLEMENTATION

 

START-OF-SELECTION.

 

  lcl_main=>start( ).

 

Here you are taking control on when your selection screen should trigger. Things you need to observe in above program

·         Your selection screen is defined as  a screen with a different number, which is 200

·         You are explicitly triggering your selection screen 200 in the START method rather than giving control to framework. (Note the other events AT SELECTION SCREEN will work as usual.)

·         When the transaction is executed; first it triggers the method specified in the transaction

 

By slowly adapting to above approached you can change your coding style to OO.

 

There can be many other ways and this is just little knowledge sharing based on my experience. Comments and suggestions are welcome.

Automatic QA and release request - part 02

$
0
0

A not too long time ago...


I had detailed in previous blog about creating a tool to analyze the source code, or the best solution, using the ATC (Abap Test Cockpit).
I mentioned about the quality and wisdom of the team of programmers that grew up.

But what's the point of having an automatic verification code? What are the gains and benefits? And the worst?

Here in the company, we allocated 2 programmers to do this activity. They were checking the code, line by line, following our standard document.
When non conformities were found it was returned by mail to the developer to proceed with the correction: Much work, numerous requests daily, many similar errors, complex routines to analyze... Add to that the worst question of all: dealing with people. Identify errors and point it in source code is like to speak ill of the mother of the programmer. The human factor was the main cause of failure in this process because not all errors will be identified and not all errors identified will be corrected or treated. Yes, It is true!

Automating these validations reduces the human factor. There are no arguments against the machine... is 0 or 1. If not corrected it will stay there. The error has to be, at least, properly argued to be considered a false positive and "accepted"  by an approver.

Important to mention that the static validation does not identify 100% of the logic and performance problems . Rather, it is almost 0. To do this validation, the tests are crucial, better doing automated tests.  But it can identify points that cause crashes, dumps into production, so with praise. And this is the focus: No dumps into production, who is allocated in support can be allocated to to improve the system.



AUTO QA.jpg

The image above shows month by month - from March to November 2013 - how transport request was handled with automatic release.


  • Green   - no problems, all right - released by request owner
  • Yellow  - False positive - released by an approver
  • Red      - Urgent or no time to correct releases, authorized by boss - released by an approver


Approvers had a lot of work but it was decreasingly month by month. We analysed yellow request to create/change our patterns or to create rules to turn it in green requests. But the main focus was to reduce the red requests number.


The developer and support teams did not accept well the solution. The big problem: change past codes. It is impossible to change old codes without test all scenarios and without automated tests. Then we adjusted the solution to skip the verification on old codes when support team creates the request and only check modified code. But the developer team must have to change old codes when they changed an old program and have to test it all. Of course, case by case was analysed to did not delay the delivery of a project.


Attention to yellow requests: it appears only once. When we registered an approval the validation don't show it anymore in the future.


Other good effect was the lower number of dumps in production.


We advanced some steps and for the next year (2014) we will do it better using ATC and others SAP standard tools.


And parallel with this initiative we started the ABAP DOJO meetings, but it will be a topic for another blog...


Automated Note Search Tool (ANST) - Quick and easy how to

$
0
0

Yesterday we had an issue on an SAP implementation. I was sure that it was a standard error, because in other implementation the functionality works perfectly.

 

On Twitter Nicolas Busson reminded me that ANST can save your life quickly and easily

 

Tweet.PNG

This video is the live implementation of the OSS Note using ANST.

 

Enqueues and Update Tasks

$
0
0

What help.sap.com doesn't tell us

I thought, SAP is pretty clear in what they tell us about the _SCOPE-parameter that can be passed to every enqueue-function modul.

The parameter can have 3 values, which means, according to help.sap.com:

 

Meaning of the _SCOPE Values

Value

Description

_SCOPE = 1

The lock belongs only to the dialog owner (owner_1), and therefore only exists in the dialog transaction. The DEQUEUE call or the end of the transaction, notCOMMIT WORKorROLLBACK WORK,cancels the lock.

_SCOPE = 2

The lock belongs to the update owner (owner_2) only. Therefore, the update inherits the lock whenCALL FUNCTION ‘…‘ IN UPDATE TASKandCOMMIT WORKare called. The lock is released when the update transaction is complete. You can release the lock before it is transferred to the update usingROLLBACK WORK.COMMIT WORKhas no effect, unlessCALL FUNCTION ‘…‘ IN UPDATE TASKhas been called.

_SCOPE = 3

The lock belongs to both owners (owner_1 and owner_2). In other words, it combines the behavior of both. This lock is canceled when the last of the two owners has released it.

 

_SCOPE Parameter (SAP Library - Architecture Manual)

 

The most interesting statement is the description for _SCOPE = 2. It says that the lock is released when the update transaction is complete.

So I understood that COMMIT WORK will implicitly release the lock. As I found out, it is not necessarily the case.

 

The test report

Consider the following test report.

The report is going to request a lock for an ABAP report. The lock argument is '1' as a dummy value. Keep in mind, that Z_UPDATE_TASK_DUMMY is an update function module that has no execution logic in my case.

 

DATA lt_enq TYPE TABLE OF seqg3.
FIELD-SYMBOLS <ls_enq> TYPE seqg3.
PARAMETERS pa_upd TYPE abap_bool AS CHECKBOX.
CALL FUNCTION 'ENQUEUE_ES_PROG'   EXPORTING     name           = '1'     _scope         = '2'   EXCEPTIONS     foreign_lock   = 1     system_failure = 2     OTHERS         = 3.
IF sy-subrc <> 0.   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
IF pa_upd = abap_true.   CALL FUNCTION 'Z_UPDATE_TASK_DUMMY' IN UPDATE TASK.
ENDIF.
COMMIT WORK AND WAIT.
CALL FUNCTION 'ENQUEUE_READ'   EXPORTING     gclient               = sy-mandt     guname                = sy-uname   TABLES     enq                   = lt_enq   EXCEPTIONS     communication_failure = 1     system_failure        = 2     OTHERS                = 3.
IF sy-subrc <> 0.   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ELSE.   IF lt_enq IS INITIAL.     WRITE 'No locks existing'.   ELSE.     LOOP AT lt_enq ASSIGNING <ls_enq>.       WRITE: <ls_enq>-gname,               <ls_enq>-garg,               <ls_enq>-gmode.       NEW-LINE.     ENDLOOP.   ENDIF.
ENDIF.

The report offers a parameter PA_UPD to the user. If checked, the update function module will be called. If not checked, there is no update task registered. Afterwards, COMMIT WORK AND WAIT is always triggered.

In the end, the report shows any locks that the current user has.

 

The ugly truth

If the report is called with the enabled checkbox, the dummy function module will be called. In the end, all locks are released.

No Locks existent.PNG

 

If the report should not execute any update function module (checkbox is deactivated), the locks are not released at all. COMMIT WORK AND WAIT has no influence here as there is no update task registered. I also included the output of SM12 in the screenshot.

No Locks released.PNG

 

The moral of the story

Locks of the update owner are not necessarily released, when COMMIT WORK has been called. It depends on wether or not an update task has been called at all. help.sap.com is not wrong with the description about _SCOPE = 2, however, someone who reads the description might get a wrong idea of how ABAP works in this very case. It took me some time to figure out what happens here. So I hope this blog post helps other developers saving some time.


Using MS word as Editor in SAP Script and Smartforms

$
0
0

 

This document details the procedure in using Microsoft Word as editor in SAP Script / Smart Forms.

 

In normal case, when you try opening the editor in Smart Forms, the following editor would appear:

1.png

 

Click on the editor button. The following editor appears:

 

2.png

 

Now to change the above editor to MS Word, do the following:

 

Go to transaction I18N (Internationalization).

 

Click on I18N Menu ->I18N Customizing ->I18N System Configuration (as shown in the screenshot

 

3.png

 

The following screen appears:

 

 

4.png

 

If you would like to use MS Word Editor in both Script and Smartforms, then check the both checkboxes

 

as shown below:

 

 

5.png

 

Click on Activate.

 

The following popup appears (related to SAPScript Editor):

 

6.png

 

Click on Yes. Now the following popup appears (related to Smartforms Editor):

 

7.png

 

Click on Yes to proceed.

 

Now the MS Word editor is available for both SAP Script and Smartforms.

 

Result: In the SmartForms:

8.png

 

 

In SAP Script:

 

 

9.png

 

 

You can anytime revert to old editor by deactivation MS Word editor option using the transaction I18N.

Troubleshooting Switch Framework issues

$
0
0

   Since SAP introduced Enhancement Package concept it is possible to switch business functions on anytime in a Netweaver system and for some cases it is possible to switch them off without downtime window via transaction SFW5.

  However sometimes activation process does not succeed and it is necessary to investigate what went wrong. For such scenarios it is sometimes complicated to get the root cause because this procedure involves Data Dictionary objects, ABAP programs, BC-Sets and others.

 

Checking for SAP Notes now we have an excellent documentation created by SAP Development team which we can understand how to check and how to evaluate each scenario.

 

1909425 - SFW- Activation error in Dictionary: clear up the inconsistencies

 

This documentation is not only a guide how to troubleshoot issues related to Business Function activation process, but a small training content where we can learn a lot. By check its information now it is much more easier to understand where issues is and how to identify a solution for this.

 

For instance, if you find inconsistencies related to BC-Sets objects in the system you can easily identify it via SFW_BROWSER transaction

Image1.png

 

I really encourage you to reserve few minutes to read this documentation.

SFTP - Connection From SAP ABAP - Findings

$
0
0

Hello ,

 

I am posting this blog to list out my findings of the possibility of Connecting to a server enabled with  SFTP from a Abap program.

I have been given this requirement from my company that i create a report and transfer it to a server which has been changed from normal FTP to SFTP.

In my previous blogs i have already listed the process of using Function Modules to transfer using normal FTP.

 

I hope this blog helps people understand the limitations of sap abap in using sftp

 

 

SAP ABAP has the Functionality to write/Read on FTP server, but there is no functionality in ABAP to read /Write on SFTP server. The code will not work with SFTP server .

 

Please refer to  SAP Note 795131 - FAQ: How to make Secure FTP communication with SAPFTP.

 

SAP recommendations

Sap’s recommendation to read/write to SFTP server is by using PI (Process Integration) solution.

 

 

Option 1

 

Server side scripting commands to write to SFTP server from SAP Application Server with the help of an open Source FTP client (WinSCP).I was able to write the script for this in a notepad .

 

code.

 

option batch abort

option confirm off

open sftp://username:password@192:192:192:192:9952   { where 192.192.192.192 is the server address and 9952 is the port number }

put F:\check.txt  { This is a local file i created that can be transferred to check the put statement}

close

exit

 

 

Note : If you want to test this code above please install WinSCP and open Winscp.com . Type the code step by step and you will connect and put the file.

 

The challenge here was that i replicate this using External Commands in SM69 in SAP .So i got to it but the issue here was our system needed some upgrades from a Basis point of view where enabling of External Commands was possible. This is how far i got .

 

Note : If any one who reads this blog has implemented External Commands from SAP to transfer SFTP please share .

 

Option 2

 

Secure File Transfer Protocol (SFTP) can be achieved using SSH (Secure Socket Shell

The flow of the data goes as follows:

  1. Firstly the SSH (client local/daemon local) is configured as per the Vendors instructions.
  2. The SSH client is installed to the client system while the SSH daemon is installed to the FTP server.
  3. The data is sent from SAPFTP to the local port of the SSH client and this makes the data secure (encrypts the data).
  4. Then the SSH client sends this data to the SSH server daemon which decrypts the data and forwards the data to the FTP server.

SSH can be configured for both Active and Passive mode of FTP communication.

 

 

Conclusion

 

There is no straight forward method to transfer to a SFTP server .

 

Thank you .

 

Please do post comments and feedback to help me improve my content.

Enemy of the State

$
0
0

01 state of fear.png

 

 

Enemy of the State

 

                             

 

·        What in the world is a “state machine”? An ABAP Example

·        How to achieve this using a previous “domain specific language” example

·          How to constantly tear apart and improve existing programs to achieve things like the above without destroying all life throughout the universe

 

Louis L'Amour once said “Nobody got anywhere in the world by simply being a TABLE OF CONTENTS

·          Introduction

·          State Machines and Design Patterns in ABAP

·          Ripping apart and refactoring existing programs

·          Example code for a State Machine framework in ABAP

·          Cliff-hanger Ending

02 off we go.png

Blog Standard

 

I have two series of blogs going at the moment. One is about the project I am working on for Baron Frankenstein, in which I hope to prove the benefits of OO development based upon unit testing. The other, into which category this one falls, is a bit more esoteric / academic / abstract and so may not be of much interest to any but the most technical minded ABAP programmers. Hopefully there are some out there.

 

I have been going through these so called “design pattern” things, and when I find one I don’t understand at all, or which seems to have no practical application in the SAP world, I try to write a blog about it, with the aim that you can’t write about something until you have truly understood it. So I attempt to find an article on the internet with an example of such a pattern, and then write an ABAP program to do the same, and then, the bit that makes me grit my teeth, see if I can’t improve on the original example. Only then am I happy I’ve got my head around the thing. Then of course, comes the really difficult part – using it in a real life situation. Only then can I truly say it is not just a piece of academic waffle.

 

Thus far I have done this with the “design by contract” pattern (if it is a pattern) http://scn.sap.com/community/abap/blog/2012/09/08/design-by-contract-in-abap, and the “decorator” which had me foxed when I first read about it http://scn.sap.com/community/abap/blog/2013/03/02/oo-design-patterns--decorator--in-abap.

 

In this blog I am going to have a word with the “state” pattern which as we will see was a total mystery to me at first, and in the future the next cab off the rank will be the “visitor” which is another one I read assorted articles about and end up none the wiser.

 

51st State Pattern

 

Some time back, on 08/01/2013 I wrote a blog about Domain Specific Languages inspired by an article from Martin Fowler.

 

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

 

This is the sequel. At the time, I wanted to see if I could implement his Java program in ABAP, and I managed to write something that worked, and I hope stayed true to the points he was trying to make.

 

If you read the blog via the link above you can see me saying again and that I had no idea what a State Machine was.

 

When I read the book “The Clean Coder” by Robert Martin he said that any programmer that knew their stuff should be able to rattle off the difference between a Mealy and a Moor State Machine, and if you did not know then why not?

 

So I looked both up on the internet, read the Wiki definitions, and still did not understand. I was starting to wonder if I was really cut out for this sort of thing.

 

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

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

 

Then I read the “Head First Design Patterns” book and they explained in great detail about the State Pattern, using such a simple example even I could get it through my thick head. At the end of this blog I will include a text file whereby I implement the exact same example but in ABAP.

 

It was exactly the same thing as far as I could see - in both examples you have a machine that is waiting for user input and changes state based upon what the user does, and the reaction of the system to a user action is not just based on what the user just did but also on the system state.

 

As an example, I used to work in a huge great skyscraper and in the morning there would be a big bunch of people in the foyer standing just before the lifts waiting for a lift to arrive at the ground floor. There were about five lifts and every one would have a green light saying that a lift had been summoned, but because the building was so tall you often had to wait quite a bit before even one would arrive,

 

Naturally, every time a new person arrived in the foyer they would push past the vast amount of people already waiting, and go up to the lifts and press one of the buttons to summon a lift. The temptation to shout “Oh my god, you are a genius – I would never have thought of that!” was overwhelming but I always managed to hold myself back.

 

It’s the same when you are waiting at a crossing and the person beside you presses the button to say they want to cross the road every two seconds until the little green man actually starts flashing. If they were to stop doing that, say they got a mobile phone call and had to interrupt their vital button pushing activity to answer it, someone else would instantly appear to take up the slack. I was happy to see mid 2013 in Moscow that you actually get a countdown so you can look at on the other side of the road as to see how many seconds until when you will be able to cross. I was even happier to see one in Brisbane for the first time last month. I don’t suppose that will stop people pressing the button repeatedly.

 

How this relates to state machines is that I suspect the following:-  that once a lift summoning button has been pressed once, the system state changes from “idle” to “lift has been summoned” and no matter how many times you press the button again I don’t think it makes any difference. At some traffic light junctions the buttons are purely cosmetic to give people something to do while they wait for the lights to change, but in that example the system state never changes, so that’s not pertinent to this example.

 

03 lift diagram.png

 

You might now expect me to talk about writing an OO lift simulation program, but that would be too obvious. I want to see if I can use the existing state machine program I wrote per the Martin Fowler example (called “Gothic Security”), copy it making minimal changes, and the result will be an ABAP version of the Java state machine program in the Head First Design Patterns book, which is a “Gumball Machine” by the way.

 

As an aside, I ask the world : is there an example of the state pattern anywhere in standard SAP? Has anyone out there written a custom program using this pattern?

 

The Abstracts of My Tears

 

That Martin Fowler program went out of its way to abstract out the technical processing from the business rule definition. The promise of OO programming is that if you depend upon abstractions, then those abstractions can be re-usable. I have no doubt you can achieve the same with procedural programming but the idea – so I am constantly told in books - is that code re-use is easier with OO.

 

At first glance it seems the “Gothic Security” program has a large chunk which could be turned into some re-usable Z classes, and used again and again whenever we want to use the state pattern.

 

The funny thing is design patterns aren’t really supposed to have their code re-used – the idea is “you can use this solution a million times over, without ever doing it the same way twice” — Christopher Alexander, a pattern is supposed to be a guide to how to do solve a problem as opposed to a fixed re-usable template.

 

In the book “refactoring to patterns” by Joshua Kerievsky the quote is “true value of this book lies not in the actual steps to achieve a particular pattern but in understanding the thought processes that lead to those steps”.

http://industriallogic.com/xp/refactoring/

 

Nonetheless the opposite argument is that the only reason design patterns is exist is because of gaps in the programming language. In the wiki entry on design patterns in the “criticism” section you can see several people who have made a huge effort to introduce language support for the major design patterns.

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

 

If I were to take ABAP as an example, it is clear that several design patterns have been “baked into” the ABAP language:-

·          Observer Pattern - in the examples (Java) I have seen you seem to have to go to quite an effort to set up the observer pattern, but ABAP has the SET HANDLER FOR command to let classes subscribe to events of other classes, so the language seems to do the hard yards for you

·          Model View Controller – SAP has always made a big thing about this, and the pattern is in fact mandatory in Web Dynpro

·          Prototype – this is all about cloning an instance of an object. In SAP this seems to be embedded at the kernel level a la SYSTEM-CALL OBJMGR CLONE me TO result.

·          Proxy – I was reading the Head First Design Patterns chapter on the proxy patterns and they were talking about the incredibly complicated Java things you need to do to get remote proxies working so remote systems can talk to each other, and my eyes glazed over, and I thought this has no relevance to me, and then the light bulb went on and I thought “hang on – I did the same thing in SAP this very day”. That is what transaction SPROXY (which lets SAP talk to PI and vice versa) is all about, built in support for a complicated design pattern.

 

This week we’re in a car park, but the rules of the game remain the same

 

The rules of the game are very simple – I needed to create a program in ABAP which does everything the “Gumball” program from Head First Design Patterns does, using the state pattern. Hang on, I hear you cry, you have not yet mentioned what those requirements are!

 

Well I am not going to. I am going to show you the code instead. One of the key elements of the whole test driven development / unit testing approach is that you should be able to grasp the requirements (what the program should do) by looking at the tests. How the program does this is something totally unrelated, and not of any interest to many business types.

 

Ch-Ch-Changes

 

Regardless of whether I was a procedural programmer or an OO one, if I had to write a Gumball Machine program, I would copy my previous Gothic security program, because it did almost exactly sort of the same thing, and then start changing it.

 

The proof of the pudding is – do you have to change LESS in an OO program?

 

I haven’t done any of this yet as of the time of writing, so I don’t know the result. I have years of experience of changing procedural programs so I know how much effort that involves. It could be that the OO version is so much easier to change it will prove the OO vs. procedural debate so conclusively I will never even wonder about it again, and use this as an example to convert all my colleagues.

 

It could be that it makes no difference at all. Either way, I am burning to know.

 

I now take a copy of my OO Gothic Security program, and begin to convert it to an OO Gumball Machine program. To start off with this seems like just a series of find and replace operations.

 

In both cases we have the business rules written down in the form of circles with arrows pointing to each other. Each circle is a possible state of the system. This is like an old children’s TV show in the UK called “runaround” where the host would shout “runaround now” and the children would dash madly from one circle to another.

 

04 Runaround.png

 

I am merrily doing my find and replace, when suddenly I am horrified to find that my whole scientific experiment has been rendered pointless by me running headlong into one of the core ways my mind works, and that is – I find it impossible to return to a program I wrote in the past, even a short while in the past, without thinking “that’s rubbish” and totally re-writing it.

 

Re-Write My Fire

 

There are two schools of thought here. I imagine the most common is the “change as little as possible to reduce your chances of breaking something” attitude, on the reasonable grounds that breaking things can get you sacked. The exact opposite opinion is mentioned in the Foreword to “Clean Code” where Fred Brooks http://en.wikipedia.org/wiki/Fred_Brooks is quoted as saying all major chunks of software should be rewritten from scratch every seven years. Then the foreword postulates that should be decreased to every seven hours. That’s going a bit far, even for me.

 

The Code Cleaner

 

I constantly mention in my blogs the “Boy Scout Rule” from Robert C. Martin about leaving code cleaner than when you found it. As an example, this very day I found the following in a program I had to fix.

 

REFRESH IT_SOMETHING.

 

…. Loads of commented out code ….

 

LOOP AT IT_SOMETHING.

 

…. Loads of code ….

 

ENDLOOP.

 

then a comment saying the above code did not seem to be working, and a helpdesk reference, from 2001 …

 

SELECT *

  FROM VBAP INTO TABLE IT_SOMETHING.

 

LOOP AT IT_SOMETHING.

 

the exact same longs line of code as in the loop above ….

 

ENDLOOP.

 

As might be imagined, when debugging this, the second loop worked a lot better than the first. I can only presume that whoever tried to fix this12 years ago did not want to touch the existing code, for fear of breaking something, so wrote some extra code below it to get the data from the database, then did a big copy and paste and everything worked, problem over.

 

However, I wouldn’t let it lie. I could have let it lie, but I didn’t let it lie. I should have let it lie, but I wouldn’t let it lie. I deleted all the commented out code, and the loop over the empty table. I might have broken something. I didn’t, but I might have. Some people would say I was evil, EVIL I TELL YOU!

 

My crimes don’t end there. It gets worse. Sometimes I even change the technology, like replacing WRITE statements with the ALV.

 

Aunty Fragile

 

I can imagine people reading the above and thinking – “hang on, you are changing not only what you are supposed to be changing, but bits of the surrounding code as well? That’s a waste of time, and more importantly money, and even more importantly introducing an unacceptable level of risk.”

 

On the face of it, that’s a valid point. The major custom programs at my company, like at many others have evolved over a long time, and just like an ancient Roman ruin, the beauty of the original has been obscured by layers and layers of mud over time, till nothing is visible of what it was supposed to do originally.

 

They are now so fragile that if you look at them sideways they stop working, and yet the constant stream of change requests never stops. If you fix one part, two seemingly unrelated parts break - but people are willing to tolerate that – it’s normal. Generally programmers would not be allowed to try and make the thing less fragile, on the grounds that it is fragile. Eventually it is deemed too dangerous to make any change at all.

 

Have a look at this blog:-http://scn.sap.com/community/abap/blog/2013/12/01/antifragile-software

 

The important point there is that the book on things that were “anti-fragile” was nothing to do with software. It was about political or biological systems which responded to change and stress by getting stronger rather than falling to pieces.

 

What Doesn’t Kill You Makes You Stronger

 

We want to reverse this – we want good old Aunty Fragile to turn the inevitable torrent of change from a disaster in waiting to a Good Thing - we want each change to make each subsequent change easier, which is the opposite of the normal order of things. Each change should make it more obvious what is going on, should reduce the complexity rather than just adding endless extra chunks of conditional logic, or should increase the separation of concerns so a change in one place is less likely to break something far away. This is the “Boy Scout Rule” once again. Now, you may say I’m a dreamer – but I’m not the only one. One day you will join us, and the ABAP world will be as one.

 

The obvious thing to do is to slowly but surely have unit tests for all your code. Then you know for sure when a change to one part breaks something else. That’s not an easy task; usually you have to break your program into bits e.g. extracting the database access statements into a separate class.  Also code with proper unit tests is twice as long as “normal” code, and so takes twice as long to write. Also if you start making changes to enable unit tests chances are you will break something. Also there is no obvious (instant) business benefit.

05 oh no.png

 

It’s not looking good, is it? I imagine very few people would get permission to make the required changes to existing programs. I work for a fairly enlightened company, so it’s all right for me, though I am moving as slowly as I can. Maybe the best people can hope for is to be allowed to write unit tests for new developments, though when anyone hears you have to write twice as much code, that idea may also go straight out the window as well.

 

I have become convinced the benefits are so great that any amount of effort is justified, but I may be crying alone in the wilderness here. Now it is time for a shameless plug.

 

06 plug.png

 

I am giving a talk on this very subject – how unit tests increase the stability of programs – at the Mastering SAP Technology conference in Melbourne which runs from 29/03/2014 to 02/04/2014 (the conference, not my talk).

 

http://www.masteringsap.com/techau

 

Is this why I have gone off the topic of state machines and started waffling on about unit tests? Is it Penry, the mild mannered janitor? Could be…..

 

Let’s presume the can is already open

 

OK, now you have added unit tests to your program, and have 100% test coverage. Well done! Now you can change anything you want with impunity, and be sure nothing breaks. Here are examples of some of the “boy scout” changes I have been making recently…

 

Clarity : I came across a chunk of code which appeared all throughout a big complex program, subtly different each time. Firstly I moved it into its own subroutine, so the behaviour became consistent, and future changes would be in one place only. This was popping up a box to get the user to enter some data, in four different circumstances. The problem was each conditional test used obscurely named variables, so I had no idea what was actually intended. I needed to know to see which, if any, of the four slightly different blocks of seemingly identical code was correct. Luckily for me, sitting opposite me at that exact instant was a guy who had been with the company for forty years. Not everybody has that luxury. Anyway, it was all obvious to him, he rattled off the four occasions when that box should pop up and why. I added this as comments at the start of the subroutine i.e. WHY this was happening, and what was going on in the real world outside of SAP that made this behaviour desirable in certain circumstances.  Armed with this knowledge I could know see which of the four blocks of code was correct – none of them. They had all started the same with one condition only, and over the years the other three conditions had been added to one or more of the blocks of code, whichever ones the programmer came across, always missing at least one.

 

Consistency : In this program were about fifty different places where pop ups were shown to the user, sometimes three or four in a row. They were mostly called with CALL SCREEN STARTING AT X Y mixed in with a few POPUP_TO_CONFIRMS. As an academic exercise I looked to see how many of the CALL SCREENS started at the same co-ordinates. It was none of them. So if a user went through four pop ups, the boxes would be all over the screen like a mad woman’s breakfast. One of my colleagues suggested this was intentional, to force the user to move their eyes around the screen so they would pay attention to the contents of the box. I suspected the truth was every pop up box had been coded by a different person over a ten year period and they each just picked a start position at random. In the same way, the code inspector warns you when a function key other than F12 is used for “cancel”. In the program I was looking at, about fifteen different function keys on different screens pointed at cancel, but F12 was nowhere to be seen.

 

Clarity (variable naming) : There was a very strange piece of code which exported some data to a Z table, and then SUBMITTED another Z program which read back that exact same data from the Z table and printed it out. That did not seem very sensible to me, so I called a function module instead; passing it the data in an IMPORT parameter, and the function module had the same code as the Z program. That broke things. It took a lot of looking but I discovered the culprit was a control variable called L_CNT. I had presumed that because it started with L it was a local variable, and indeed it was declared at the start of a subroutine, but on close inspection it was declared thus:-

STATICS: L_CNT.

So, it was a global variable really. That was fine when the code was called via SUBMIT, it was always initial, but that did not work so well during repeated calls to a function module. Upon even further investigation, the variable was declared as a STATIC, and named as it was, because the whole section of code had been cut and pasted from the internet as example code of how to print a SMARTFORM with multiple copies in the same spool.

 

Clarity (Variable Naming) : Whilst debugging a problem I found a variable with a name like COMBINED_WEIGHT which gave me a false idea of what it might represent. After a lot of searching I found that this global variable was only filled if a user had been given a pop up and manually entered the weight. So I changed the variable name to MANUALLY_ENTERED_WEIGHT. After a good old FIND/REPLACE the code made a lot more sense in certain places, and made no sense at all in others, and then I zeroed in on the bits where it made no sense and before long my problem was solved. This is following a principle I read somewhere on the internet called “make bad code look bad”

http://www.joelonsoftware.com/articles/Wrong.html

 

Clarity (Comments) : This next one I found two weeks ago and it made my bang my head against the wall. I had written a comment above a line of code as a warning. The context is, this code is inside an implementation of a standard SAP BADI.

"I have discovered if you call SET_DATA more than one time in this user
"exit then you lose some of the changes e.g. blanking out the quantity

IF lf_change_needed= abap_true.
    im_item
->set_data(ls_item).
 
ENDIF.

I thought that was a useful warning – again it did not say WHAT the code was doing, but highlighted a problem to be avoided.

 

Then the next programmer came along a few months later and needed to insert a big chunk of code. He decided the best place for the new code would be between the comment and the IF/ENDIF statement above. It was a big long section of code, so the poor old comment was moved up well away from the command it related to, so it no longer made much sense, it just seems like a random musing. Even worse I have often seen code deleted, and the comment remain. That is why comments that say what the code do are considered bad, they are pretty much redundant, as you can see what it is the code is doing, and then people change the code but not the comment and you end up with a comment saying “the below code does X” when in fact the below code is now doing Y.

 

Uncle Fragile

 

I have seen, in assorted IT departments, then whenever they have an IT project they do a post mortem and come up with some “lessons” learned, which they then add to a big spreadsheet which nobody ever looks at again, and then the same mistakes are made the next time.

 

Taking my cynical hat off for a moment, maybe it would be an idea, that, whenever you find that something you have to do in regard to custom program maintenance could be placed in the “difficult to change” basket and so took a lot of work or whatever, try and ask yourself why it was difficult, and how you can redesign things so the next change is not so hard.

 

There are obvious ones like language – if you have hard coded EN or DE as the language and then have to make the program work for Poland or whoever, then you change the hard coding to SY-LANGU and then you don’t have to change it again for the next country.

 

The next most obvious is duplicate code – if you find you have to change six pieces of identical code in different places, then that is a signal to encapsulate the common code, so next time you only have to change it in one place.

 

I won’t go on with all the example, I am sure you get the idea, they range from obvious one like that and work up to complicated things like replacing conditional logic with subclasses or factories or something.

 

Again this is an antimatter idea – instead of the normal situation that every change makes the program more complicated so the next change is more difficult, every change makes the next one easier. Just like antimatter, when this sort of idea meets traditional ways of doing things, the entire universe explodes.

 

Let’s get back, back, back, back to the start

 

That was one hell of a diversion. You’ve probably already forgotten what caused me to go off topic – so I will summarise:-

 

·          I have a burning desire to totally re-write old programs whenever I need to change small bits of them, what the OO gurus call “merciless refactoring”

·          In the normal run of things this is suicidally dangerous, you are bound to break something and find yourself on the dole queue

·          However, with sufficient unit tests in place you can make changes with impunity, and be sure you haven’t broken anything.

·          Writing those unit tests is one hell of an effort. I personally think it’s worth it.

 

Now, let us return to the topic of state machines. I had an existing purportedly re-usable state machine template, I am going to extract the re-usable bits to Z classes, change them, and then see if the original program still works. I will be able to tell if the original program still works because it is 100% covered by unit tests (that’s not as impressive as it sounds, as it only does one thing, so only has one test).

 

Please don’t bother trying to find it – it’s not there!

 

So, what’s missing from my “gothic” ABAP program? This fine day I was on the train reading about state machines in the appendix to “Head First Object Orientated Analysis and Design”. The irony is I am the only one on the train reading a book made of paper, every single other person is playing with their electronic device. There was a tramp on the train the other night, drinking straight from a bottle of spirits and arguing loudly with the voices in his head, and even HE had a tablet of some sort. The only reason I’ve got an IPAD is that I won one at an SAP conference. Maybe that is where the gentleman on the train got his from.

 

Anyway, in the “Head First” description of state diagrams they say that when you have an arrow pointing from one state to another, you can have a “guard expression” which means you can’t follow that arrow unless the statement is true. There are no such conditions in the “Gothic” program, but I need them in the “Gumball” program – for example you can’t dispense a gumball if the machine has run out of gumballs. This is going to be such a common requirement it needs to be catered for in my re-usable Z classes.

 

The other main difference is that in the “Gumball” example there are all sorts of error messages shown to the user (of the machine) when they do something wrong like try and pull the dispensing lever without having entered any money first. In the “Gothic” program there is no output to any user whatsoever. So that needs to be addressed as well.

 

Having unit tests also forces you to do things “properly”. For example, you can’t have any sort of output to the user during a unit test, not even a pop up information message – it makes the test fall over – so you have to isolate the user interface layer into its own class and have a fake in the unit test so no actual user interaction takes place.  So when the user does something that is not allowed I will have to throw an exception, and in the real code handle that by popping up an error message, and in the unit test just catch the exception and call an appropriate ASSERT method.

 

Class of the Mohicans

 

To start off with I am going to re-create the local classes in the “Gothic” program as Z classes and try and restrain myself from changing much just yet.

The first class I will call ZCL_SM_ABSTRACT_EVENT on the grounds that Martin Fowler named it “abstract event” though it isn’t really an abstract class. This was how it looked when it was a local class:-

 

CLASS lcl_abstract_eventDEFINITION.
 
PUBLIC SECTION.
   
METHODS: constructor IMPORTING id_nameTYPE string
                                  id_code
TYPE char04,
            get_name    RETURNING
value(rd_name) TYPE string,
            get_code    RETURNING
value(rd_code) TYPE char04.

 
PRIVATE SECTION.
   
DATA: md_nameTYPE string,
          md_code
TYPE char04.

ENDCLASS."Abstract Event Definition

 

As we are no longer in Java, we don’t really need the GET methods,we can make the attributes public and read-only, and set them in the constructor as per normal. I’ll make both attributes strings. The name is for humans to look at, the code is for an external system (i.e. a machine) to look at.

 

I then – in the Gothic program - replace the references to the local class with references to the Z class, and comment out the local class definition and implementation. I have to fluff around changing assorted data definitions from CHAR04 for a string but once I am done everything compiles. Next I take the Program -> Test -> Unit Test option to see if I have broken anything. I have not, so on I will go.

 

I then turn the two local classes for inbound events from external systems and outbound commands to external systems into ZCL_SM_EVENT_IN and ZCL_SM_COMMAND_OUT. These seems pointless at the moment, as all they do is inherit from the ZCL_SM_ABSTRACT_EVENT without changing anything, but that’s the way it was in the original program and I am not changing anything much yet.

 

Once again I comment out the definition of the local classes, do a find / replace exercise to bring in the Z classes, and once there are no syntax errors do the unit test thing again. I am going to take this as read from now on – I will be doing a unit test check after every change from now on, but won’t bore you by constantly saying so.

 

INTERFACE lif_external_system.

 
METHODS: poll_for_event RETURNING value(rd_event) TYPE string,
          send_command 
IMPORTING id_commandTYPE string.

ENDINTERFACE.

 

This feels better, all the literature tells me interfaces are good, subclasses are bad. I turn this into a Z interface and change the parameter names to have the word CODE at the end, to remind me I am talking to an external system i.e. a machine. The actual implementation will always be different per program, as the external system will always be different.

 

I move onto to abstracting the “state” object into a Z class. Since the idea is to make the code read as much like English as possible I rename most of the methods. Here is the local class before I turn it into a Z class. I will be attaching a SAPLINK nugget at the end of this blog with all the code anyway.

 

CLASS lcl_stateDEFINITION.
 
PUBLIC SECTION.
   
METHODS: constructor                IMPORTING id_name                TYPE string,
*--------------------------------------------------------------------*
* Define Behaviour
*--------------------------------------------------------------------*
            state_reached_sends_command
IMPORTING io_command            TYPE REF TO zcl_sm_command_out,
            state_changes_after_event 
IMPORTING io_event              TYPE REF TO zcl_sm_event_in
                                                  io_target_state       
TYPE REF TO lcl_state,
*--------------------------------------------------------------------*
* Execute Behaviour
*--------------------------------------------------------------------*
            changes_state_after_event 
IMPORTING id_event_code          TYPE string
                                        RETURNING
value(rf_bool)        TYPE abap_bool,
            target_state_after_event   
IMPORTING id_event_code          TYPE string
                                        RETURNING
value(rf_target_state) TYPE REF TO lcl_state,
            send_outbound_commands     
IMPORTING io_external_system    TYPE REF TO zif_sm_external_system.

 
PRIVATE SECTION.
   
DATA: md_name              TYPE string,
          mt_outbound_commands
TYPE STANDARD TABLE OF REF TO zcl_sm_command_out,
          mt_transitions     
TYPE g_tt_transitions.

ENDCLASS."State Definition

 

I had to also extract the “transition” class at the same time, as the two classes depend on each other so much – a potential sign of bad design so I have been told.

 

CLASS lcl_transitionDEFINITION.
 
PUBLIC SECTION.
   
METHODS: constructor    IMPORTING io_source_state  TYPE REF TO lcl_state
                                      io_trigger_event 
TYPE REF TO lcl_event
                                      io_target_state 
TYPE REF TO lcl_state,
            get_source    RETURNING
value(ro_sourceTYPE REF TO lcl_state,
            get_target    RETURNING
value(ro_targetTYPE REF TO lcl_state,
            get_trigger    RETURNING
value(ro_trigger) TYPE REF TO lcl_event,
            get_event_code RETURNING
value(ro_code)    TYPE char04.

 
PRIVATE SECTION.
   
DATA: mo_source_state  TYPE REF TO lcl_state,
          mo_target_state 
TYPE REF TO lcl_state,
          mo_trigger_event
TYPE REF TO lcl_event.

ENDCLASS."Transition Definition

 

I get rid of all the “get” methods, as in ABAP the same can be achieved with a public read-only attribute, I don’t need the GET_EVENT_CODE method at all.

 

With those two local classes extracted the “Gothic” program is looking a lot shorter, and there is still more we can take out. Remember we want to extract everything re-usable and only leave the specifics of this program.

 

The original program had a separate “state machine” class, which dealt with one thing only, which is handling reset events which bring the system back to its starting point. I can see that is a useful thing but I think I’ll rename it to better indicate what it’s purpose in life is.

 

That only leaves the “controller” class to be turned into a Z class. I checked all the method implementation and there are no commands that are specific to the application at hand, everything is nicely generic.

 

CLASS lcl_controllerDEFINITION.
 
PUBLIC SECTION.
   
METHODS: constructor      IMPORTING io_external_system TYPE REF TO zif_sm_external_system
                                        io_system_resetter
TYPE REF TO zcl_sm_system_resetter,
            handle_command   
IMPORTING io_event_code      TYPE string,
            get_current_state RETURNING
value(ro_state)    TYPE REF TO zcl_sm_state.

 
PRIVATE SECTION.
   
DATA: mo_external_systemTYPE REF TO zif_sm_external_system,
          mo_system_resetter
TYPE REF TO zcl_sm_system_resetter,
          mo_current_state 
TYPE REF TO zcl_sm_state.

   
METHODS: transition_to IMPORTING io_target_state TYPE REF TO zcl_sm_state.

ENDCLASS."ControllerDefiniton

 

I will turn the “getter” method into a read only public attribute. I need to change the name of “handle command” to make it clear this is responding to an event coming into our system from an external system.

 

I am now down to only three classes – the external system, which naturally will be different every time, the class under test itself, in this case the gothic security system and the unit testing class. I like to think I have achieved one of the aims of OO - namely to separate the things that change from the things that stay the same. The low level “boiler plate” code has been broken down into classes so small they are completely generic and hidden in Z classes. The bulk of what remains in the application code itself is concerned with shouting to the rooftops what this program is supposed to achieve (the test class), and how it behaves to achieve that goal (the main configuration method).

 

Mind your Domain Specific Language

 

The next step is to ensure that what remains looks as much like plan English as possible, so our deadly enemies the business experts can read it. I used to be a business expert myself, so I am my own worst enemy. This next level of separation could be described as “separating what can be read by a human from what can be read by an ABAPer”.

 

The first bit is knocking out the code which contains phrases like INHERITING FROM and INTERFACES and TYPE REF TO and hiding it from those who would not know what it means. They know not what it means, only that when they gaze upon the evil that is OO code, they know that it is A Bad Thing and must be purged from the land. We can isolate that in INCLUDES.

 

The next stage is making what remains read more like English. For example, as things stand at the moment we have:-

 

  unlocked_panel_state->state_changes_after_event(io_event        = panel_was_closed
                                                  io_target_state
= idle ).

 

In the above “sentence” we have the words “state” and “event” twice in a row, prompting the comment “I heard you the first time” and the habit I have got into of prefixing input parameters for object instances with IO_ has to go in the dustbin. I still say there are times when you need that sort of thing, but not here, it just confuses the non-ABAP reader. The only business experts who I know of who like that naming convention are the Seven Dwarves. They wanted me to write the parameter as follows:-

 

IO_IO = ITS_OFF_TO_WORK_WE_GO.

 

After renaming a few object definitions (of states) and the name and parameters of a method in the ZCL_SM_STATE class the code reads a lot more like an English sentence:-

 

    panel_is_unlocked->state_reached_sends_command(unlock_the_panel ).
    panel_is_unlocked
->state_reached_sends_command(lock_the_door ).
    panel_is_unlocked
->state_changes_after(event          = panel_was_closed
                                            to_target_state
= idle ).

 

This constant meddling with things is a bit like the Japanese “Kanban” exercise, which, if I’ve got it right, means making a non-stop stream of really minor improvements to something forever and never being satisfied with the end result.

 

So I’m happy enough now, the next part is the vitally exciting part of the exercise, seeing how easy it is to assemble a “Gumball” program out of this framework with hopefully minimal effort. The first thing I’ll do is – hang on, what’s that coming over the hill? Is it a monster? Is it a monster?

 

07 monster.png

 

Will the gumball machine program ever get written?

Will the monster destroy everything?

Will it’s “always done it this way” programming style and big teeth quash the OO revolution?

 

To find out, tune in to the next exciting episode of “Enemy of the State Machine” coming to an SCN near you in a month or so…

 

 

 

 

P.S. I tried to attach a SAPLINK nugget with the example "Gothic" program and the tiny Z classes needed to make it run, but it looks like SCN does allow the uploading of files of type .NUGG. Why would it? It's not as if this is an SAP centric website or anything. I'll attach the nugget file as a text file and see if anyone can work with that.

 

 

Combine several Spool request and create PDF - Transfer to local Desktop

$
0
0

Hello all ,

 

I have developed a program that will combine several spool request that are otf and create a single PDF . This PDF is then transferred to the desktop . If you want to transfer this file using FTP please refer to my other blogs .

 

This is a working program and if you run it with multiple spool number which are otf it will create a single PDF .

 

Thank you

 

 

******************************************

 

*&---------------------------------------------------------------------*
*& Report  Z_COMBINE_SPOOL_REQUESTS
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

REPORT  z_combine_spool_requests.

TYPES: BEGIN OF blob,
        line(80) TYPE x,
        END OF blob.

*** PDF conversion Declaration .
DATA: pdf_size      TYPE so_obj_len,
       pdf_content   TYPE solix_tab,
       pdf_xstring   TYPE xstring,
       v_xstring     TYPE xstring,
       v_text        TYPE localfile,
       rq            TYPE tsp01,
       bin_size      TYPE i,
       temp_bin_size TYPE i,
       dummy         TYPE TABLE OF rspoattr,
       cancel,
       doctab LIKE docs OCCURS 1 WITH HEADER LINE,
       numbytes TYPE i,
       arc_idx LIKE toa_dara,
       pdfspoolid LIKE tsp01-rqident,
       jobname LIKE tbtcjob-jobname,
       jobcount LIKE tbtcjob-jobcount,
       is_otf,
       client LIKE tst01-dclient,
       name LIKE tst01-dname,
       objtype LIKE rststype-type,
       type LIKE rststype-type,
       get_size_from_format,
       bindata TYPE TABLE OF blob WITH HEADER LINE,
       temp_bindata TYPE TABLE OF blob WITH HEADER LINE,
       result TYPE TABLE OF text WITH HEADER LINE,
       filesize TYPE i,
       convcount TYPE i,
       textlines LIKE tline OCCURS 100 WITH HEADER LINE,
       tab1 TYPE TABLE OF soli,
       tab  TYPE TABLE OF soli WITH HEADER LINE,
       doc  LIKE TABLE OF docs,
       pdf  LIKE TABLE OF tline,
       file TYPE string ,
       otf  TYPE TABLE OF itcoo WITH HEADER LINE,
       eof TYPE soli ,
       lines TYPE i .


***Selection Screen
DATA :   spoolno TYPE tsp01-rqident,
          temp_spool TYPE tsp01-rqident.

SELECT-OPTIONS : range FOR spoolno NO INTERVALS.



LOOP AT range.

   temp_spool = range-low.

   CALL FUNCTION 'RSPO_RETURN_SPOOLJOB'
     EXPORTING
       rqident                    = temp_spool
*       FIRST_LINE                 = 1
*       LAST_LINE                  =
*       DESIRED_TYPE               =
*     IMPORTING
*       REAL_TYPE                  =
*       SP_LANG                    =
     TABLES
       buffer                     = tab1
*       BUFFER_PDF                 =
    EXCEPTIONS
      no_such_job                = 1
      job_contains_no_data       = 2
      selection_empty            = 3
      no_permission              = 4
      can_not_access             = 5
      read_error                 = 6
      type_no_match              = 7
      OTHERS                     = 8
             .
   IF sy-subrc <> 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
*         WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
   ENDIF.

   DESCRIBE TABLE tab1 LINES lines.
   READ TABLE tab1 INDEX lines INTO eof.
   DELETE tab1 INDEX lines.

   APPEND LINES OF tab1 TO tab.


   LOOP AT tab.
     CLEAR otf.
     otf = tab.
     APPEND otf.
   ENDLOOP.

*append lines of tab1 to bindata.
   REFRESH tab1.
   REFRESH tab.

ENDLOOP.
APPEND eof TO otf.




CALL FUNCTION 'CONVERT_OTF_2_PDF'
  EXPORTING
    use_otf_mc_cmd               = 'X'
*   ARCHIVE_INDEX                =
  IMPORTING
    bin_filesize                 = bin_size
   TABLES
     otf                          = otf
     doctab_archive               = doc
     lines                        = pdf
* EXCEPTIONS
*   ERR_CONV_NOT_POSSIBLE        = 1
*   ERR_OTF_MC_NOENDMARKER       = 2
*   OTHERS                       = 3
           .
IF sy-subrc <> 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
*         WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.
*
*



CALL METHOD cl_gui_frontend_services=>gui_download
   EXPORTING
     filename                = 'C:\sample.PDF'
     filetype                = 'BIN'
   CHANGING
     data_tab                = pdf
   EXCEPTIONS
     file_write_error        = 1
     no_batch                = 2
     gui_refuse_filetransfer = 3
     invalid_type            = 4
     no_authority            = 5
     unknown_error           = 6
     header_not_allowed      = 7
     separator_not_allowed   = 8
     filesize_not_allowed    = 9
     header_too_long         = 10
     dp_error_create         = 11
     dp_error_send           = 12
     dp_error_write          = 13
     unknown_dp_error        = 14
     access_denied           = 15
     dp_out_of_memory        = 16
     disk_full               = 17
     dp_timeout              = 18
     file_not_found          = 19
     dataprovider_exception  = 20
     control_flush_error     = 21
     not_supported_by_gui    = 22
     error_no_gui            = 23
     OTHERS                  = 24.
IF sy-subrc <> 0.
   WRITE sy-subrc.
ENDIF.

Too many cooks spoil the broth

$
0
0

Dear Solution Managers(Friends),


"This is the limit of tolerance". I am writing this blog to collect people's point of view especially ABAPer how they tackle functional person's logic when they are more than one.

While working on xyz project / functionality / report many times I am encounter such kind of situation where I am getting too many logic for the single task from so many functional person. Each of them tries proves that they are correct. I don't know on what basis they are providing their functional logics and how to proofing them it as correct one. I know many of us have gone through suck kind of situations I would like to know what to do when such sort of time has arrived.

User wise case to case system is neither preferable nor reliable (Strongly not possible). Sometimes I am scared to think what to do if one more selection do exists (Execute report for which user)



 

 

It's totally horrible to dream also. Sometime I feel like SAP has provided so many standard procedures (functional logic) why not to follow one them which is suitable and compatible. Then go behind and thinking ok it's for Z and all is better if user needs something another.




I am eagerly wanted to know the experience faced by others. I am getting same kind of experience or I am the only the case and if second option is activated then you may think that I am Alien on earth.



 

-Avirat

Boxed components and memory efficiency

$
0
0

What's a boxed component?

 

I read about them first in this document - http://scn.sap.com/docs/DOC-26231. Then I read the release notes - you do read the release notes when you upgrade to a new abap version, don't you? It's worth doing, as you can find some very nice language enhancements. I read the sap help, but couldn't quite get what the point of them was, nor how they worked.

 

Then I read this excellent blog SAP ABAP TYPE BOXED, New Type Category 7.02 Ehp2 - ABAP Help Blog that explains their functionality very clearly, but just now, I've created my first, real world use of the concept. So I'll share it.

Buffering data

 

I've got some master data - materials and time dependent attributes. I've got a few million records of data that contain materials and dates, and I want to find the attributes for those material, at that particular date. The records I've got are in no particular order, may refer to the same material many times, and may well have materials that don't have any master data. Also, the materials in my records are only a subset of all the possible materials.


Got that? Good.


The volume of master data is such that I cannot read all the data into an internal table in one go. So what I do is maintain a buffer that starts off empty. For a given material, I first check the buffer. If it isn't there, I check the database. If I still don't find anything I create a record in the buffer with a flag "not found", If I do find something, then I add it to the buffer.


Memory


So, my line structure is something like: MATERIAL, CORRECTED_COST, CORRECT_CURRENCY, NOT_FOUND. Here's some ABAP:

TYPES: BEGIN OF corrected_cost_ty,                    MATERIAL,                    CORRECTED_COST,                    CORRECTED_CURRENCY,                    NOT_FOUND,               END OF corrected_cost_ty


Now, the problem is that if a material is NOT_FOUND, then it has no correct cost. So for all the NOT_FOUND data, there's these two fields which never contain anything, but will be taking up space, nonetheless. This is where boxing comes in.


I create a type "attributes" which has CORRECTED_COST and CORRECT_CURRENCY, then I redo my corrected_cost_ty like this.

TYPES: BEGIN OF attributes_ty,        CORRECTED_COST,         CORRECTED_CURRENCY,      END OF attributes_ty.

TYPES: BEGIN OF corrected_cost_ty,
         MATERIAL,         ATTRIBUTES type attributes_ty BOXED,         NOT_FOUND,       END OF corrected_cost_ty

Now, when I don't have any attributes for a material (because it's NOT_FOUND), memory is only allocated to the MATERIAL and NOT_FOUND.


The only downside is that I have to refer to my attributes as attributes-corrected_cost.  I'd like to be able to use something like

INCLUDE TYPE attributes_ty BOXED.


But I guess we'll have to wait a while for that to happen.






How called code can strangely influence your own program flow

$
0
0

The Problem

Because ABAP is a language with (too) many possibilities, called procedures can strangely affect the flow of the calling program. In some cases, even a trace does not clarify the behavior.

Sometimes this can be hard to figure out.

 

Known Cases

The cruel thing with the following commands is that they are performed anywhere, but affect later! So looking at the last performed command does not help.

 

set screen

If the code you are calling via a dynpro performs set screen 0, your dynpro will be left after your PAI has completely run.

If the called code performs set screen <nnnn> (<>0), this dynpro will be called after your PAI has completely run.

 

leave [to] screen

If the called code performs leave screen, the last set screen will be performed, and the calling program will not be continued.
If in this case the last set screen is 0, see set screen 0.

If in this case the last set screen <> 0, this screen will be performed.

If the called code performs leave to screen <nnnn> the same is true for dynpro nnnn.

 

suppress dialog

If you are calling code in your PBO that performs suppress dialog, the PAI of your dynpro will be processed as if the user would have hit enter (but he did not) although you even don´t see your dynpro (you still see the last dynpro).

 

message

As ABAPers we know that the message command can influence many many things, e. g. if the called program sends a message of type E your PAI-Module gets interrupted and no PBO is performed, but the program does not stop. But this command can do so much more strange things, this is why I call it the monster command .

 

Others

Not such cruel, but also in this context interesting statements are:

  • call transaction
  • call screen
  • leave
  • leave program
  • leave list-processing
  • leave to current transaction
  • leave to list-processing
  • leave to transaction
  • submit
  • stop
  • exit from step-loop

 

A real life Example

We called BAPI_PROCORD_CHECK_MAT_AVAIL and after that call, the PAI was still processed and after PAI the program easily has ended.
See here: BAPI_PROCORD_CHECK_MAT_AVAIL affecting screen flow.

 

How does that help?

Well, next time I see such a strange behavior, I will set break points at the following commands:

  • set screen
  • leave
  • suppress dialog
  • message
  • call transaction
  • call screen
  • submit
  • stop
  • exit from step-loop

 

What did I miss?

Did I miss something? If you have more commands that could be interesting here, please tell me.

If you have any questions or other notes, please tell me.

 

Greets

Stefan

Create Efficient Code using renewed ABAP

$
0
0

Hi friends,

 

I am writing this just to share what I had learned from the TECH ED 203 Bangalore.

 

The renewed ABAP has come up with efficient coding or increasing the productivity and readable code.

 

Here is a few examples of he same.

 

Old Method for declaring

 

Field Symbols

 

FIELD-SYMBOLS <line> LIKE LINE of itab.

 

LOOP AT itab ASSIGING  <line>.

....

END LOOP.

 

New Method for declaring

 

LOOP AT itab Assigning SYMBOL(<line>).

 

....

ENDLOOP.

 

Constructor ExpressionsNEW()

 

Old

DATA oref TYPE REF to class.

CREATE OBJECT oref Exporting...

 

New way

 

DATA (oref) = NEW class(....).

DATA oref TYPE REF to class.

oref = NEW#(....).

 

Table Expressions


Old Method

 

READ TABLE itab INDEX idx INTO wa.

 

New Merhod

 

wa = itab [ dix ].

 

There are many more this is how much I could remember and will start including all as I go through the notes and examples.

FBLXX NEW FIELD

$
0
0

Transaction Code : FIBF

 

 

 

 

 

 

 

SE11

RFPOS- APPEND STRUCTURE

 

 

 

Create your structure as you need

 

Append another structure also in RFPOSX but includes same fields with Appended structure in RFPOS

 

 

 

Run the program RFPOSXEXTEND.


Ready

Automated Note Search Tool (ANST) - Quick and easy how to

$
0
0

Yesterday we had an issue on an SAP implementation. I was sure that it was a standard error, because in other implementation the functionality works perfectly.

 

On Twitter Nicolas Busson reminded me that ANST can save your life quickly and easily

 

Tweet.PNG

This video is the live implementation of the OSS Note using ANST.

 

Enqueues and Update Tasks

$
0
0

What help.sap.com doesn't tell us

I thought, SAP is pretty clear in what they tell us about the _SCOPE-parameter that can be passed to every enqueue-function modul.

The parameter can have 3 values, which means, according to help.sap.com:

 

Meaning of the _SCOPE Values

Value

Description

_SCOPE = 1

The lock belongs only to the dialog owner (owner_1), and therefore only exists in the dialog transaction. The DEQUEUE call or the end of the transaction, notCOMMIT WORKorROLLBACK WORK,cancels the lock.

_SCOPE = 2

The lock belongs to the update owner (owner_2) only. Therefore, the update inherits the lock whenCALL FUNCTION ‘…‘ IN UPDATE TASKandCOMMIT WORKare called. The lock is released when the update transaction is complete. You can release the lock before it is transferred to the update usingROLLBACK WORK.COMMIT WORKhas no effect, unlessCALL FUNCTION ‘…‘ IN UPDATE TASKhas been called.

_SCOPE = 3

The lock belongs to both owners (owner_1 and owner_2). In other words, it combines the behavior of both. This lock is canceled when the last of the two owners has released it.

 

_SCOPE Parameter (SAP Library - Architecture Manual)

 

The most interesting statement is the description for _SCOPE = 2. It says that the lock is released when the update transaction is complete.

So I understood that COMMIT WORK will implicitly release the lock. As I found out, it is not necessarily the case.

 

The test report

Consider the following test report.

The report is going to request a lock for an ABAP report. The lock argument is '1' as a dummy value. Keep in mind, that Z_UPDATE_TASK_DUMMY is an update function module that has no execution logic in my case.

 

DATA lt_enq TYPE TABLE OF seqg3.
FIELD-SYMBOLS <ls_enq> TYPE seqg3.
PARAMETERS pa_upd TYPE abap_bool AS CHECKBOX.
CALL FUNCTION 'ENQUEUE_ES_PROG'   EXPORTING     name           = '1'     _scope         = '2'   EXCEPTIONS     foreign_lock   = 1     system_failure = 2     OTHERS         = 3.
IF sy-subrc <> 0.   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
IF pa_upd = abap_true.   CALL FUNCTION 'Z_UPDATE_TASK_DUMMY' IN UPDATE TASK.
ENDIF.
COMMIT WORK AND WAIT.
CALL FUNCTION 'ENQUEUE_READ'   EXPORTING     gclient               = sy-mandt     guname                = sy-uname   TABLES     enq                   = lt_enq   EXCEPTIONS     communication_failure = 1     system_failure        = 2     OTHERS                = 3.
IF sy-subrc <> 0.   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ELSE.   IF lt_enq IS INITIAL.     WRITE 'No locks existing'.   ELSE.     LOOP AT lt_enq ASSIGNING <ls_enq>.       WRITE: <ls_enq>-gname,               <ls_enq>-garg,               <ls_enq>-gmode.       NEW-LINE.     ENDLOOP.   ENDIF.
ENDIF.

The report offers a parameter PA_UPD to the user. If checked, the update function module will be called. If not checked, there is no update task registered. Afterwards, COMMIT WORK AND WAIT is always triggered.

In the end, the report shows any locks that the current user has.

 

The ugly truth

If the report is called with the enabled checkbox, the dummy function module will be called. In the end, all locks are released.

No Locks existent.PNG

 

If the report should not execute any update function module (checkbox is deactivated), the locks are not released at all. COMMIT WORK AND WAIT has no influence here as there is no update task registered. I also included the output of SM12 in the screenshot.

No Locks released.PNG

 

The moral of the story

Locks of the update owner are not necessarily released, when COMMIT WORK has been called. It depends on wether or not an update task has been called at all. help.sap.com is not wrong with the description about _SCOPE = 2, however, someone who reads the description might get a wrong idea of how ABAP works in this very case. It took me some time to figure out what happens here. So I hope this blog post helps other developers saving some time.

Viewing all 943 articles
Browse latest View live