Skip to Content

Example for a Singleton Class used in BW 7.x transformations

Object-oriented development is more and more often used and more common use. The advantages of object-oriented programming have spread over the world in the last years. With this triumph new methodologies and approaches to software development went along. One of these approaches are so-called “Design Patterns“.

Design Patterns are more and more often used and discussed by ABAP developers. In this blog I will give you an example for the usage of the design pattern “Singleton” in BW 7.x transformations. I assume you have at least a basic knowledge of ABAP/OO and know the basic ABAP conecpts, espeicially logical lock objects. Furthermore I assume you are familiar with transforamtions and start- and endroutines.

 The idea of “Design Patterns” has been developed in the 90s by the so-called “Gang of four” Erich Gamma, Richard Helm, Ralph E. Johnson and John Vlissides to improve the object-oriented software development. They discovered repeating “patterns” of software and gave abstract solutions for these “patterns” or reusable pieces of code. For more information on design patterns I can only highly recommend the book “Design Patterns – Elements of Reusable Oject-Oriented Software” by these famous four. You can order it here .

One of these design patterns is “Singleton”. You can find an explanation of this special design pattern here in SDN wiki and also an easy sample implementation.

But where’s the link to BI? When could a Singleton class be useful in BW staging?

 You all know these from your daily work: You have your transformation between your DSOs and Cubes. You have created a code to derive e.g. the customer market from customer. Usually you solve such look-ups with a combination of “SELECT” into internal table and “READ TABLE” afterwards.

Your startroutine looks something like this:


$$ begin of 2nd part global – insert your code only below this line </pre><pre>data: gt_customer type table of /bi0/pcustomer.</pre><pre>$*$ end of 2nd part global – insert your code only before this line </pre><pre>


</pre><pre> CLASS routine IMPLEMENTATION</pre><pre>*


</pre><pre></pre><pre>*


</pre><pre>CLASS lcl_transform IMPLEMENTATION.</pre><pre>


</pre><pre> Method start_routine</pre><pre>*


</pre><pre> Calculation of source package via start routine</pre><pre>*


</pre><pre> <-> source package</pre><pre>*


</pre><pre>METHOD start_routine.</pre><pre>=== Segments ===</pre><pre>FIELD-SYMBOLS:</pre><pre><SOURCE_FIELDS> TYPE tys_SC_1.</pre><pre>DATA:</pre><pre>MONITOR_REC TYPE rstmonitor.</pre><pre>$$ begin of routine – insert your code only below this line </pre><pre>select * from /bi0/pcustomer into table gt_customer.</pre><pre>@</pre><pre>$$ end of routine – insert your code only before this line </pre><pre>ENDMETHOD. “start_routine</pre><p>In the field routine you do the read table and read the attribute you need:</p><pre>CLASS lcl_transform IMPLEMENTATION.</pre><pre>METHOD compute_0CUST_MKT.</pre><pre>* IMPORTING</pre><pre>* request type rsrequest</pre><pre>* datapackid type rsdatapid</pre><pre>* SOURCE_FIELDS-CUSTOMER TYPE /BI0/OICUSTOMER</pre><pre>* EXPORTING</pre><pre>* RESULT type tys_TG_1-CUST_MKT</pre><pre>DATA:</pre><pre>MONITOR_REC TYPE rsmonitor.</pre><pre>$$ begin of routine – insert your code only below this line </pre><pre>data: ls_customer type /bi0/pcustomer.</pre><pre>read table gt_customer into ls_customer</pre><pre>with key customer = source_fields-customer.</pre><pre>RESULT = ls_customer-cust_mkt.</pre><pre>$$ end of routine – insert your code only before this line </pre><pre>ENDMETHOD. “compute_0CUST_MKT</pre><p>I bet you have this customer look-up not only in this transformation but in many other transformations. One solution to avoid duplicated code or coding islands is using a function module. You define your internal table as static. By the first call of the function module the select on the customer table is executed, you buffer the data in your internal table and then you do the look-up for your customer marketstomer with a “READ TABLE” statement.</p><p>Fllowing ABAP-function does the same:</p><pre>FUNCTION ZKC_LOOKUP_CUSTOMER.</pre><pre>*”


</pre><pre>“Lokale Schnittstelle:</pre><pre>” IMPORTING</pre><pre>” REFERENCE(P_CUSTOMER) TYPE /BI0/PCUSTOMER-CUSTOMER</pre><pre>” EXPORTING</pre><pre>” REFERENCE(P_CUST_MKT) TYPE /BI0/PCUSTOMER-CUST_MKT</pre><pre>*”—


statics: gt_customer type table of /bi0/pcustomer.

data: ls_customer type /bi0/pcustomer.

if gt_customer is initial.

select * from /bi0/pcustomer into table gt_customer.

endif.

read table gt_customer into ls_customer

with key customer = p_customer.

if sy-subrc = 0.

p_cust_mkt = ls_customer-cust_mkt.

else.

p_Cust_mkt = ''.

endif.

ENDFUNCTION.

Hey, that’s fine – a good solution.

But what if you have more than one look-up? If you want to know if anyone has written a look-up for a material group from material master? You have to ask your colleague. Ideally one of your colleagues has created a function group where all look-up FMs are collected. You still have to find out the name of the right FM.

Wouldn’t it be easier if there is one central point for all of your look-ups and easy to call?

Right –  ABAP Classes can help you! You create a class “ZCL_LOOKUP_CENTRAL” and for each look-up you define a method, e.g. GET_MATERIALGROUP_FROM_MATERIAL” or “GET_CUSTOMERGROUP_FROM_CUSTOMER”. You define class private attributes for your tables. In the constructor of the class you execute “SELECT” into your class attributes. In the method you execute “READ TABLE” and return the result. 

Your class in ABAP-code would look like that:

 Class: ZCL_LOOKUP_CENTRAL

Attributes
Private Attribute
GT_CUSTOMER  Inst  customer data  TYPE TABLE_CUSTOMER

Methods

Public Methods
CONSTRUCTOR
Instance methode

method CONSTRUCTOR.
  select * from /bi0/pcustomer into table gt_customer.
endmethod.

GET_MARKET_FROM_CUSTOMER
Instance methode

Importing-Parameter
 PI_CUSTOMER TYPE /BI0/PCUSTOMER-CUSTOMER 

Exporting-Parameter
 PE_CUST_MARKET TYPE /BI0/PCUSTOMER-CUST_MKT 

method GET_MARKET_FROM_CUSTOMER.
data: ls_customer type /bi0/pcustomer.

  read table gt_customer into lS_customer
    with key customer = pi_customer.
  if sy-subrc = 0.

 pe_cust_market = ls_customer-cust_mkt.

 else.

pe_cust_market = ''.

endif.
endmethod.

In the start- or end-routine of your transformation you create an object of this class and then call the method to lookup the data.

Your transformation looks like that:


$$ begin of 2nd part global – insert your code only below this line </pre><pre>data: gt_customer type ref to zcl_lookup_central.</pre><pre>‘ end routine

METHOD end_routine.


=== Segments ===</pre><pre>FIELD-SYMBOLS:</pre><pre><RESULT_FIELDS> TYPE tys_TG_1.</pre><pre>DATA:</pre><pre>MONITOR_REC TYPE rstmonitor.</pre><pre>$*$ begin of routine – insert your code only below this line </pre><pre>create object gt_customer.</pre><pre>loop at RESULT_PACKAGE assigning <result_fields>.</pre><pre>CALL METHOD GT_CUSTOMER->GET_MARKET_FROM_CUSTOMER</pre><pre>EXPORTING</pre><pre>PI_CUSTOMER = <RESULT_FIELDS>-customer</pre><pre>IMPORTING</pre><pre>PE_CUST_MARKET = <RESULT_FIELDS>-cust_mkt.</pre><pre>modify RESULT_PACKAGE from <result_fields>.</pre><pre>endloop.</pre><pre>$$ end of routine – insert your code only before this line

ENDMETHOD. "end_routine

 Hey- good, this works!

But what does this mean in praxis?  For each new datapackage a new instance of this class is created. One and the same table is selected over and over again, but it’s very improbable that data has changed. So it would be good to have a “static” class. Unfortunately classes can’t be static, methods only. It would be very sufficient to have only one instance of this class. At the first call of this class the data is selected from the tables and for each next call only the results will be returned, no more select on the tables. Now we are back to Design Patterns. The pattern “Singleton” is a possible solution!

You call your class with a static class method “GET_INSTANCE”. This method is the only way to access an existing instance or to create a new one. Your constructor method has to be private.

Your “SINGLETON” class will look like that:

Class: ZCL_LOOKUP_CENTRAL

Attribute
Private Attributes
GT_CUSTOMER  Inst  customer data  TYPE TABLE_CUSTOMER
GO_LOOKUP    Stat  myself         TYPE REF TO ZCL_LOOKUP_CENTRAL

Methods

Public Methods

GET_MARKET_FROM_CUSTOMER
Description: returns market from customer
Instance method

Importing-Parameter
 PI_CUSTOMER TYPE /BI0/PCUSTOMER-CUSTOMER 
Exporting-Parameter
 PE_CUST_MARKET TYPE /BI0/PCUSTOMER-CUST_MKT

method GET_MARKET_FROM_CUSTOMER.
data: ls_customer type /bi0/pcustomer.
  read table gt_customer into lS_customer
    with key customer = pi_customer.
  pe_cust_market = ls_customer-cust_mkt.
endmethod.

GET_INSTANCE
Description: returns instance
Static Method
Exporting-Parameter
 P_INSTANCE TYPE REF TO ZCL_LOOKUP_CENTRAL 

method GET_INSTANCE.
  if go_lookup is initial.
    create object go_lookup.
  endif.
  p_InstaNCE = go_LOOKUP.
endmethod.

 

As you can see, the only difference to your old implementation is creating a new static class method for instance creation, adding a private attribute for the instance and making the public constructor method private. So you can create very easily your own singleton classes from existing ones.

In your transformation youchange one line only in your end routine, bold marked:

 

METHOD end_routine.

=== Segments ===</p><p>FIELD-SYMBOLS:</p><p><RESULT_FIELDS> TYPE tys_TG_1.</p><p>DATA:</p><p>MONITOR_REC TYPE rstmonitor.</p><p>$*$ begin of routine – insert your code only below this line

call method ZCL_LOOKUP_CENTRAL=>GET_INSTANCE

importing p_instance = gt_customer.</p><p>loop at RESULT_PACKAGE assigning <result_fields>.</p><p>CALL METHOD GT_CUSTOMER->GET_MARKET_FROM_CUSTOMER</p><p>EXPORTING</p><p>PI_CUSTOMER = <RESULT_FIELDS>-customer</p><p>IMPORTING</p><p>PE_CUST_MARKET = <RESULT_FIELDS>-cust_mkt.</p><p>modify RESULT_PACKAGE from <result_fields>.</p><p>endloop.</p><p>$$ end of routine – insert your code only before this line

ENDMETHOD. “end_routine

 

This solution has still one pitfall! In case of multi-user environment or parallele execution two users/processes can call the instance creation method at the same time. In this case our coding doesn’t prevent the creation of two classes. With parallel execution of the Data transfer process exactly this happens. The first data package will be exceuted by the job scheduler and the next data packages are also scheduled and executed. Depending on your settings you have towo or more jobs whol will be executed at the same time and therefore also the startroutine. To prevent this you have to work with semaphores or “lock” techniques.

One easy solution to get out of this situation is to create a SAP logical lock with according “ENQUEUE” and “DEQUEUE” FMs. I assume that you are familiar with creating lock objects, so I don’t explain it here. For more details on lock objects refer to SAP ABAP Help or to respective ABAP courses and books. Now it is very easy to prevent the parallel creation of your object. In your  “GET_INSTANCE” method, the first thing you do is try to set an exclusive lock!

If you’ve set the lock, brilliant, you’re the owner, create the object and release the lock!

IF not, well, your ENQUEUE-FM will return with error. Now wait a few seconds or as long as you assume your constructor needs to execute and try again.

Your GET_INSTANCE method should now look something like that, use of ENQUEUE- and DEQUEUE_FMs is marked bold

 

method GET_INSTANCE.

* set the exclusive lock


CALL FUNCTION ‘ENQUEUE_EZ_KH_TEST’


EXPORTING


MODE_ZKH_LOCK = ‘X’


** IS_LOCK =</pre><pre>* X_IS_LOCK = ‘ ‘</pre><pre>_SCOPE = ‘2’</pre><pre>_WAIT = ‘X’</pre><pre>* COLLECT = ‘ ‘</pre><pre>EXCEPTIONS</pre><pre>FOREIGNLOCK = 1</pre><pre>SYSTEM_FAILURE = 2</pre><pre>OTHERS = 3</pre><pre>.</pre><pre>IF SY-SUBRC <> 0.</pre><pre>* do here something or raise error</pre><pre>ELSE.</pre><pre> we got the lock, everything ok

if go_lookup is initial.

create object go_lookup.

endif.

p_InstaNCE = go_LOOKUP.

* release the lock


CALL FUNCTION ‘DEQUEUE_EZ_KH_TEST’


EXPORTING


MODE_ZKH_LOCK = ‘X’


** IS_LOCK =</pre><pre>* X_IS_LOCK = ‘ ‘</pre><pre>_SCOPE = ‘3’</pre><pre>* _SYNCHRON = ‘ ‘</pre><pre>* _COLLECT = ‘ ‘</pre><pre>.</pre><pre>ENDIF.*

@

endmethod.

This solution has one little disadvantage in a “load-balanced” or “multi-server” environment. In a load-balanced environment you don’t know on which server your object creation task ends up. In this environment in combination with multi-user or parallele execution you can have one instance of your class on each server.

 

 

6 Comments
You must be Logged on to comment or reply to a post.
  • One comment:

    you will most likely have more than one instance at a time.

    Why? -> Because of parallel processing of the DTP.

    If 3 parallel batch processes are started, they run in seperate memory address spaces, so each process will have one instance, and your lookup table gets loaded 3 times.
    It’s still a good optimization, it’s just sometimes important to bear in mind that you cannot share data between ALL your data packages – unless you process them sequentially, using one batch process only…

    • Right, that is very important to keep in mind.

      I’m always happy to see people advocating for a more object-oriented approach, but given the process issue I’m not sure what advantage this approach has over using a static method and static attribute, or creating your object instance in the global section of the transformation class using a CLASS DATA statement, which should also result in the instance being shared between requests within the process. Any reason to choose one of these three approaches over the other?

    • Hendrik,

      thanks for your comment. I tested this solution with a DTP with 3 parallel processes. The really nice thing is, that the GET_INSTANCE method controls if a new instance of the class is created or not. As long as one process (in the same memory area that’s true) holds a reference to the class the next process (in the same memory area) takes this instance. With this instance all of the data is kept in memory. You’re right, if the process is finished and no more data is read from the class, a new instance would be created by the next process. So if you span lifetime of the class from startroutine to endroutine you, define it transforamtion global, a reference is kept as long as the datapackage is executed. My observation was, that the class had been instantiated only once on the server and two or more processes shared the class. You are right, in a load-balanced environment, where you don’t know on which server your process will be executed you have more than one instance.

      Regards,
      Juergen

  • Hi,

    this is a quite good explanation on how to provide frequently-used data.

    You can use a static class with select in the class-constructor to get the same result. Coding is easier as you will never need to create an instance.

    I do not think that locking objects is the best approach to save data space.

    This is worth to consider SHARED OBJECTS. The new book of Thomas Jung and Rich Heilman covers this. The online preview provides the chapter on use of shared objects.

    I think the singleton pattern has been created in a one-user one-memory context. In AS ABAP we have LUWs using processes, here we should go beyond.

    Clemens

    • Hi Clemens,

      thanks for comment and your thoughts.
      I could have used static class, but my doubt was that the class-constructor will be called by each call of my static method. So I couldn’t span lifetime of the class from start- to endroutine and make use of Re-using the data. I think data will be lost if the static method has been executed and data will be selected over and over again in a multi-process environment.

      Shared objects are very interesting, I will have a closer look to it.

      As far as I know singleton pattern has been created for the mutli-user one-memory context. It should be possible to hold the data only once in the system/memory, as long as at least one object holds a reference to this class. That’s what is done with the get_instance method.

      Regards,
      Juergen

      • Hello Juergen,
        “but my doubt was that the class-constructor will be called by each call of my static method”

        No it doesn’t … You can check this by putting a breakpoint in the CLASS-CONSTRUCTOR method 🙂