Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Sougata
Active Contributor
 

 

Introduction


I do not see many developers using the new, modern and powerful enhancements to the ABAP language yet as a result most of these modern features are not leveraged in the applications which, I believe, were made with an intention to modernise the language as well as simplify the development and maintenance of the application lifecycle.

The intention of this blog series is to highlight the benefits of functional programming by embracing the new ABAP features (and some old techniques as well which are powerful enough but not very popular for reasons unknown) to write smart, exciting, reusable, sustainable code and avoid writing boring, repetitive (often violating the Don't Repeat Yourself (DRY) principle or the Separation of Concern (SoC) principle), redundant, old fashioned (and often procedural) code which is a nightmare to maintain and/or to be able to extend these applications.

Back to the Future


We will start with one of the powerful features which is already "old" i.e. it's been there in the system surely for more than a decade but I do not see many developers using it at all - it is the Resumable Exception. I will demonstrate the handling of a same business scenario exception with and without its Resumable option to understand the difference and also the benefits of a modern development design which leverages these powerful features ultimately saving time & effort which equals money to the customer in the long run.



Business Scenario


The business scenario is quite simple. A list of employees are to be reported with their respective Employee Group (EMPTYP), Salary amount (SALARY) and Phone Numbers (PHONE).

However, for EMPTYP = 'Contractor', there will be no salary record found in the system (as it is managed by their recruitment agencies) however other information of these contractors like Employee Type and Phone Numbers are still required to be reported.

Also, if Employee Group (EMPTYP) data is not found in the system (data not maintained), they need to be reported as exceptions for those employees.

Technical Design


In the context of the business scenario, the internal table we will be looping on contains References to employee objects for all Types of employees (Employee Type) i.e. Full-Time, Part-Time, Contractors, Casual and so on.

In each iteration of the loop, we will read all required employee data and move them to build another internal table to display the results.

Things to note in the design phase:

  • The contractor group has no pay details stored in the system as they are managed externally - the business however would still like to see the other available data in the system for them.

    • Enter Exception Objects - we could treat this scenario as an exception and handle them separately however we need to make sure that the exception object is managed in a way to be able to still process the employee attributes for which the data is available.



  • As the attribute Employee Type is quite important to the organisation, any employee with missing Employee Type record needs to reported as exceptions so that their master data can then be maintained.

    • Sounds like a straight forward case of an Exception Object which needs to be handled and then excluded from processing of the report.




Implementation


Exception handling WITH the Resumable addition


Report ztest.

CLASS lcl_employee DEFINITION FINAL CREATE PUBLIC.

PUBLIC SECTION.

TYPES: BEGIN OF empl_data,
empid TYPE int4, "Employee ID
emptyp TYPE string, "Org Assignment data
salary TYPE decfloat16, "Pay data
phone TYPE numc10, "Communication data
END OF empl_data,
empl_data_t TYPE SORTED TABLE OF empl_data WITH UNIQUE KEY empid.

METHODS constructor IMPORTING VALUE(i_empid) TYPE int4.
METHODS get_data RETURNING VALUE(rs_result) TYPE empl_data
RAISING RESUMABLE(cx_no_data_found).

PRIVATE SECTION.

DATA emp_id TYPE int4.

METHODS get_emptyp RETURNING VALUE(r_result) TYPE string
RAISING cx_no_data_found.
METHODS get_salary RETURNING VALUE(r_result) TYPE decfloat16
RAISING RESUMABLE(cx_no_data_found).
METHODS get_phone RETURNING VALUE(r_result) TYPE numc10.
METHODS get_emp_id RETURNING VALUE(r_result) TYPE int4.

ENDCLASS.

CLASS lcl_employee IMPLEMENTATION.

METHOD constructor.
me->emp_id = i_empid.
ENDMETHOD.

METHOD get_data.
rs_result = VALUE #( empid = me->get_emp_id( )
emptyp = me->get_emptyp( )
salary = me->get_salary( )
phone = me->get_phone( )
).
ENDMETHOD.

METHOD get_emptyp.
r_result = SWITCH #( me->get_emp_id( )
WHEN 1 THEN |Full-Time|
WHEN 2 THEN |Part-Time|
WHEN 3 THEN |Contractor|
WHEN 4 THEN |Casual|
ELSE THROW cx_no_data_found(
rel_proc_id = CONV #( me->get_emp_id( ) ) )
).
ENDMETHOD.

METHOD get_phone.
r_result = SWITCH #( me->get_emptyp( )
WHEN `Full-Time` THEN |1234567890|
WHEN `Part-Time` THEN |5678901234|
WHEN `Casual` THEN |7890123456|
ELSE |0399999999|
).
ENDMETHOD.

METHOD get_salary.
r_result = SWITCH #( me->get_emptyp( )
WHEN `Full-Time` THEN 50000
WHEN `Part-Time` THEN 25000
WHEN `Casual` THEN 5000
ELSE THROW RESUMABLE cx_no_data_found(
rel_proc_id = CONV #( me->get_emp_id( ) ) )
).
ENDMETHOD.

METHOD get_emp_id.
r_result = me->emp_id.
ENDMETHOD.

ENDCLASS.

DATA extract_t TYPE lcl_employee=>empl_data_t.
DATA error_t TYPE string_table.

START-OF-SELECTION.

DATA(all_employees_t) = VALUE int4_table( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ).

LOOP AT all_employees_t REFERENCE INTO DATA(dref).
TRY.
INSERT NEW lcl_employee( dref->* )->get_data( ) INTO TABLE extract_t.

CATCH BEFORE UNWIND cx_no_data_found INTO DATA(lx_no_data).
IF lx_no_data->is_resumable = abap_true.
"Resumable Exception was raised
RESUME.
ELSE.
"Non-Resumable Exception was raised
error_t = VALUE #( BASE error_t ( lx_no_data->get_text( ) ) ).
ENDIF.
ENDTRY.
ENDLOOP.

cl_demo_output=>new( )->write( extract_t )->write( error_t )->display( ).

 

Exception handling WITHOUT the Resumable addition


The implementation of the Local Class (below code snippet) needs to be slightly different in this case as the calling program would want to know for which specific data the exception was raised whereas the above implementation (with the Resumable addition) simply does not care.
CLASS lcl_employee DEFINITION FINAL CREATE PUBLIC.

PUBLIC SECTION.
TYPES: BEGIN OF empl_data,
empid TYPE int4, "Employee ID
emptyp TYPE string, "Org Assignment data
salary TYPE decfloat16, "Pay data
phone TYPE numc10, "Communication data
END OF empl_data,
empl_data_t TYPE SORTED TABLE OF empl_data WITH UNIQUE KEY empid.

METHODS constructor IMPORTING VALUE(i_empid) TYPE int4.
METHODS get_emptyp RETURNING VALUE(r_result) TYPE string
RAISING cx_no_data_found.
METHODS get_salary RETURNING VALUE(r_result) TYPE decfloat16
RAISING cx_no_data_found.
METHODS get_phone RETURNING VALUE(r_result) TYPE numc10.
METHODS get_emp_id RETURNING VALUE(r_result) TYPE int4.

PRIVATE SECTION.
DATA emp_id TYPE int4.

ENDCLASS.

CLASS lcl_employee IMPLEMENTATION.

METHOD constructor.
me->emp_id = i_empid.
ENDMETHOD.

METHOD get_emptyp.
r_result = SWITCH #( me->get_emp_id( )
WHEN 1 THEN |Full-Time|
WHEN 2 THEN |Part-Time|
WHEN 3 THEN |Contractor|
WHEN 4 THEN |Casual|
ELSE THROW cx_no_data_found(
rel_proc_id = CONV #( me->get_emp_id( ) ) )
).
ENDMETHOD.

METHOD get_phone.
r_result = SWITCH #( me->get_emptyp( )
WHEN `Full-Time` THEN |1234567890|
WHEN `Part-Time` THEN |5678901234|
WHEN `Casual` THEN |7890123456|
ELSE |0399999999|
).
ENDMETHOD.

METHOD get_salary.
r_result = SWITCH #( me->get_emptyp( )
WHEN `Full-Time` THEN 50000
WHEN `Part-Time` THEN 25000
WHEN `Casual` THEN 5000
ELSE THROW cx_no_data_found(
rel_proc_id = CONV #( me->get_emp_id( ) ) )
).
ENDMETHOD.

METHOD get_emp_id.
r_result = me->emp_id.
ENDMETHOD.

ENDCLASS.

And the calling program (the consumer) is different as well.
Report ztest.

DATA extract_t TYPE lcl_employee=>empl_data_t.
DATA error_t TYPE string_table.

START-OF-SELECTION.

DATA(all_employees_t) = VALUE int4_table( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ).

LOOP AT all_employees_t REFERENCE INTO DATA(dref).

DATA(ref) = NEW lcl_employee( dref->* ).

TRY.
DATA(extract) = VALUE lcl_employee=>empl_data(
empid = ref->get_emp_id( )
emptyp = ref->get_emptyp( )
phone = ref->get_phone( )
).

CATCH cx_no_data_found INTO DATA(lx_no_data).
error_t = VALUE #( BASE error_t ( lx_no_data->get_text( ) ) ).
CONTINUE.
ENDTRY.

"Note that the call to the method GET_SALARY( ) is separated
TRY.
extract = VALUE #( BASE extract
salary = ref->get_salary( )
).
CATCH cx_no_data_found INTO lx_no_data.
ENDTRY.

INSERT extract INTO TABLE extract_t.
ENDLOOP.

cl_demo_output=>new( )->write( extract_t )->write( error_t )->display( ).

 

Flexibility of the Resumable Exceptions


Now another similar requirement comes along in the near future but this time the Contractor group is required to be excluded from the report when their salary records are not found in the system and they need to be reported as exceptions as well. All other requirements remain the same.

New Implementation


    - Changes to the Resumable Exception handling


To meet this requirement, the developer who used the Resumable option achieves it very quickly by making minimal changes to the calling program as seen in the code snippet below.

The only change he does is that he handles the Resumable exception (that was raised by the method) as a normal exception object without having to modify the local class; but most importantly, he does not disturb the main processing of the calling program (the consumer).
Report ztest_new.

DATA extract_t TYPE lcl_employee=>empl_data_t.
DATA error_t TYPE string_table.

START-OF-SELECTION.

DATA(all_employees_t) = VALUE int4_table( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ).

LOOP AT all_employees_t REFERENCE INTO DATA(dref).
TRY.
INSERT NEW lcl_employee( dref->* )->get_data( ) INTO TABLE extract_t.

"This time around it was not handled as a Resumable Exception
CATCH cx_no_data_found INTO DATA(lx_no_data). "<--Handled normally
error_t = VALUE #( BASE error_t ( lx_no_data->get_text( ) ) ).
ENDTRY.
ENDLOOP.

cl_demo_output=>new( )->write( extract_t )->write( error_t )->display( ).

 

   - Changes to the Non-Resumable Exception handling


Now the implementation for the calling program (the consumer) needs to change to accommodate the changes to the business requirement.

The developer who did not use the Resumable option has to now modify the processing logic of the consumer to accommodate the changes in the business requirement.
Report ztest_new.

DATA extract_t TYPE lcl_employee=>empl_data_t.
DATA error_t TYPE string_table.

START-OF-SELECTION.

DATA(all_employees_t) = VALUE int4_table( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ).

LOOP AT all_employees_t REFERENCE INTO DATA(dref).

TRY.
INSERT VALUE #(
LET ref = NEW lcl_employee( dref->* ) IN
empid = ref->get_emp_id( )
emptyp = ref->get_emptyp( )
phone = ref->get_phone( )
salary = ref->get_salary( )
)
INTO TABLE extract_t.

CATCH cx_no_data_found INTO DATA(lx_no_data).
error_t = VALUE #( BASE error_t ( lx_no_data->get_text( ) ) ).
ENDTRY.

ENDLOOP.

cl_demo_output=>new( )->write( extract_t )->write( error_t )->display( ).

 

Conclusion


We have demonstrated the difference between raising and handling of an exception object with and without the Resumable addition/option.

We have also demonstrated the benefits of using Resumable exceptions to simplify developments.

Resumable option of the Exception object is a powerful feature and they are extremely flexible yet simple to implement. They can save developers a lot of time and effort especially in regards to reusability of common objects / framework in the system as well as simplifying implementations across the SAP system landscape.

In the next blog of this series we will focus on some of the important principles of OO development and apply those concepts on the modern (smart) ABAP.
26 Comments
Labels in this area