What is a Class Runner?
A class runner is a great tool to test code snippets and run code directly from ADT. You can even write a simple log to the console in Eclipse.
What can be improved?
When using multiple systems and complex projects I got annoyed at some point in time, that I don't know from which system the last log was, when it was written by which runner and if the last execution has finished or failed maybe.
Thats when I started my own class runner that have some improved capabilities.
How does it look like?
The output looks like this:
◉ You see when, what, by whom and where it was started.
◉ For every log entry you get a timestamp.
◉ You see when the execution finished.
◉ All times and dates are formatted in your personal date and time format
What about the ABAP code? Your code gets defined in the logic() method.
CLASS zcl_runner_demo DEFINITION
PUBLIC
INHERITING FROM zcl_base_runner
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
METHODS: logic REDEFINITION.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_runner_demo IMPLEMENTATION.
METHOD logic.
write( `Hello World` ).
write( `this is a demo of the enhanced class runner` ).
write( 123 ).
ENDMETHOD.
ENDCLASS.
Methods write() and out->write()
The write() method has the same signature as the out->write() method, you can even access the original out->write() if needed.
METHOD logic.
write( `Hello World` ).
write( `this is a demo of the enhanced class runner` ).
write( 123 ).
out->write( `bare write` ).
ENDMETHOD.
of course you can pass structured and table data to write(), like you can do to out->write()
METHOD logic.
DATA ls_airport type /dmo/airport.
write( `Hello World` ).
ls_airport = value #( airport_id = 'SAP' city = 'Dokkerland' ).
write( ls_airport ).
write( name = 'Info about airport' data = ls_airport ).
ENDMETHOD.
Uncaught exceptions
Method logic is secured against uncaught exceptions.
METHOD logic.
write( `Hello World` ).
raise EXCEPTION new cx_abap_invalid_name( ).
ENDMETHOD.
It logs the behaviour:
How does it look under the hood?
It is basically an abstract class that wraps the original ADT class runner:
"! <p class="shorttext synchronized" lang="en">Base Runner</p>
"! Improved class runner with enhanced logging capabilities
CLASS zcl_base_runner DEFINITION
PUBLIC
ABSTRACT
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun .
"! <p class="shorttext synchronized" lang="en">This method implements your logic</p>
"! You can use { .METH:write } with enhanced capabilities or the { .DATA:out }->write( ) for plain logging.
"! @raising cx_root | <p class="shorttext synchronized" lang="en">any exception not caught, will be handled in the runner.</p>
METHODS logic ABSTRACT
RAISING cx_root.
CLASS-METHODS convertuuid
IMPORTING
str TYPE string
RETURNING VALUE(rv_uuid) TYPE sysuuid_c32.
PROTECTED SECTION.
DATA out TYPE REF TO if_oo_adt_classrun_out.
"! <p class="shorttext synchronized" lang="en">wrapper for out->write( )</p>
"! this enhances the default function by writing a timestamp.
"! this method should be used in {@link .METH:logic }
"! @parameter data | <p class="shorttext synchronized" lang="en"></p>
"! @parameter name | <p class="shorttext synchronized" lang="en"></p>
METHODS write
IMPORTING
data TYPE any
name TYPE string OPTIONAL.
PRIVATE SECTION.
"! <p class="shorttext synchronized" lang="en">determine the current timestamp and returns as string</p>
"! <ul><li>in users timezone</li><li>in users prefered format</li></ul>
"! @parameter rv_dateandtime | <p class="shorttext synchronized" lang="en"></p>
CLASS-METHODS getCurrentDateandTimeFormatted
RETURNING VALUE(rv_dateandtime) TYPE string.
"! <p class="shorttext synchronized" lang="en">draws a horizontal line on the console</p>
"!
"! @parameter out | <p class="shorttext synchronized" lang="en"></p>
CLASS-METHODS horizontalLine
IMPORTING out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.
CLASS zcl_base_runner IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
me->out = out.
horizontalline( out ).
TRY.
out->write( |Start on { xco_cp=>current->tenant( )->get_url( io_type = xco_cp_tenant=>url_type->ui )->get_host( ) } runner { cl_abap_classdescr=>get_class_name( me ) } by { cl_abap_context_info=>get_user_formatted_name( ) }| &&
| at { getcurrentdateandtimeformatted( ) }| ).
CATCH cx_abap_context_info_error INTO DATA(lc_context_error).
"handle exception
out->write( 'Error occured in determine current user:' ).
out->write( lc_context_error->get_text( ) ).
ENDTRY.
TRY.
me->logic( ).
CATCH cx_root INTO DATA(lc_error).
"handle exception
write( 'Error occured in executing the logic:' ).
write( lc_error->get_text( ) ).
DATA(previous) = lc_error->previous.
IF lc_error->previous IS BOUND.
write( name = 'Previous' data = lc_error->previous->get_text( ) ).
ENDIF.
ENDTRY.
out->write( |Done at { getcurrentdateandtimeformatted( ) }| ).
horizontalline( out ).
ENDMETHOD.
METHOD write.
DATA(descr_ref) = cl_abap_typedescr=>describe_by_data( data ).
IF name IS INITIAL.
IF descr_ref IS INSTANCE OF cl_abap_elemdescr.
out->write( data = |{ getcurrentdateandtimeformatted( ) }: { data }| ).
ELSE.
out->write( data = |{ getcurrentdateandtimeformatted( ) }:| ).
out->write( data = data ).
ENDIF.
ELSE.
IF descr_ref IS INSTANCE OF cl_abap_elemdescr.
out->write( data = |{ getcurrentdateandtimeformatted( ) }: { Name }:{ data }| ).
ELSE.
out->write( data = |{ getcurrentdateandtimeformatted( ) }: { Name }:| ).
out->write( data = data ).
ENDIF.
ENDIF.
ENDMETHOD.
METHOD getcurrentdateandtimeformatted.
DATA tsp TYPE tzntstmps.
DATA(lo_unix_timestamp) = xco_cp=>sy->unix_timestamp( ).
DATA(lo_moment) = lo_unix_timestamp->get_moment( xco_cp_time=>time_zone->user ).
tsp = lo_moment->as( xco_cp_time=>format->abap )->value.
rv_dateandtime = |{ tsp TIMESTAMP = USER }|.
ENDMETHOD.
METHOD horizontalline.
out->write( repeat( val = '-' occ = 120 ) ).
ENDMETHOD.
METHOD convertuuid.
DATA(lo_uuid) = xco_cp_uuid=>format->c36->to_uuid( str ).
rv_uuid = xco_cp_uuid=>format->c32->from_uuid( lo_uuid ).
ENDMETHOD.
ENDCLASS.
No comments:
Post a Comment