Introduction
Classic batch jobs on the ABAP Application Server for S/4HANA Cloud and the BTP ABAP Environment have been replaced by application jobs. From what I could research myself, this new framework builds on the existing classical batch job functionality adding a modern layer on top and also some extensions. For ABAP developers working in cloud systems, automating tasks and setting up periodic maintenance jobs remains a valid requirement. However, the new application job framework introduces some challenges that I, too, encountered.
In this article, I’ll briefly summarize the framework, share the issues I faced while working with application jobs, and explain how I resolved them. For those interested in learning more, I’ve included links to introductory tutorials and GitHub repositories.
ABAP Jobs – Classic vs. New
I have tried to summarize the main features of the classic and new way to work with jobs. The most significant change, I believe, is the introduction of two new ABAP artifacts: catalog and template. Using them is straightforward. An ABAP class containing the job logic is developed and linked to a catalog. This catalog is then linked to a template, and finally the template is used for scheduling the job manually or programmatically. Catalogs also provide exit points to perform checks, set up value help for parameters, or send job notifications. The template stores input values for each application job.
Figure 1 – Classic vs. new
Challenges in Working with Application Jobs and Possible Solutions
Like other aspects in the ABAP Cloud, working with the new application job framework comes with some obstacles, but these are not unsolvable.
Adding visible application log to the application job
This is the simplest issue to solve and came up because I initially overlooked a parameter in the log-saving method. In the method save_log() of class CL_BALI_LOG_DB there is a parameter called assign_to_current_appl_job, which when set to true automatically links the log created during the run of an application job to the monitoring of the job in the Fiori app “Application Jobs”, making it visible there.
Figure 2 – How to link application log to an application job
Figure 3 – Application log for an executed job
Scheduling a background job with a technical user
Scheduling application jobs programmatically is possible with cl_apj_rt_api=>schedule_job(), but not all user types can do this. If you need a technical user to schedule jobs to avoid dialog work process timeouts, it’s not currently allowed in ABAP Cloud (as of November 2024).
To work around this, you can create an asynchronous start mechanism::
Instead of scheduling the job directly, the technical user adds an entry in a custom Z* table, with all the required information about the job to be started;A separate periodic job (running every one minute, 10 minutes or any other interval depending on your needs), launched by a normal business user reads the above mentioned custom Z* table and starts immediately the job. This way, your job is effectively run without requiring a direct start from the technical user
Preventing multiple instances of a job from running simultaneously
A common requirement for periodic jobs is to ensure only one instance runs at a time. While in the on-premise ABAP server you could use report RSBTONEJOB2 to prevent overlap, this isn’t available in the ABAP cloud environment. However, with some code, you can implement a similar solution (source code is in the GitHub link provided):
First you identify the data about the current running job with: cl_apj_rt_api=>get_job_runtime_info( );Then you check for other jobs running from the same catalog with: cl_apj_rt_api=>find_jobs_with_jce( catalog );If another instance is running, stop the current instance to prevent overlap.
Debugging an application job
One limitation of the new framework is the inability to debug jobs. In the classic SM37 transaction, you could use the jdbg command to debug a background job. Currently, the new framework lacks this capability to the best of my knowledge.
Initially, I tried using log points to log variable contents in the background work process, but this approach failed because ABAP Cloud doesn’t allow LOG-POINT statements. Additionally, while you can add dynamic log points in Eclipse, this requires authorization for object S_DYNLGPTS, which isn’t API-released and therefore couldn’t be added to my role.
To overcome this, I created a custom variable-tracking tool. It stores variable contents in a custom table at runtime and displays them in a simple Fiori app. By adding just one line of code, developers can trace variable content at runtime:
zcl_os_ajob_watch=>trace( )->itab( name = ‘INTERNAL_TABLE_DEMO’ var = itab_demo ).
zcl_os_ajob_watch=>trace( )->structure( name = ‘STRUCTURE_DEMO’ var = structure_demo ).
The Fiori app shows the stored variable data per job run, with options to delete recordings as needed:
Figure 4 – Visualizing variable content saved at runtime
Custom Tools for a Smoother Development Process
To tackle all these challenges mentioned above, I built custom small tools with the following principles in mind:
They should never interfere with job execution (exceptions are caught without propagation).They should use method chaining, inspired by the XCO Library, for easy implementation and removal when no longer needed.They should have short, meaningful descriptions and keywords for easy usage without additional documentation.
Using these tools, the job class implementation might look like this demo code below:
METHOD if_apj_rt_exec_object~execute.
” This is a DEMO job execution – it will execute just some dummy checks
” Set from the beginning what object/subobject and external ID you want to use for logging
zcl_os_job_log=>set_info_for_current_session(
obj = appl_log_job-obj “‘ZOS_DEMO_JOB’
sobj = appl_log_job-subobj “‘JOB_LOG’
xid = CONV #( zcl_os_appl_job_util=>get_current_job_name( ) ) ).
” One demo success log
zcl_os_job_log=>log( )->add_message(
severity = if_bali_constants=>c_severity_information
id = appl_log_job-msg_class
number = 002 )->save_log( ). ” This is a demo success message.
” === First I want to make sure that this job is not running already
IF zcl_os_appl_job_util=>is_job_already_running( info_job-catalog ) = abap_true.
zcl_os_job_log=>log( )->add_free_text(
text = |An instance of this job is already running. Will be SKIPPED!|
severity = if_bali_constants=>c_severity_warning )->save_log( ).
RETURN.
ENDIF.
” One demo error log
zcl_os_job_log=>log( )->add_message(
severity = if_bali_constants=>c_severity_error
id = appl_log_job-msg_class
number = 001 )->save_log( ). ” This is a demo error message.
” ==== Test saving structure content
SELECT SINGLE FROM i_country
FIELDS *
WHERE country = ‘RO’
INTO (structure_demo).
zcl_os_ajob_watch=>trace( )->structure(
name = ‘STRUCTURE_DEMO’
var = structure_demo ).
” ==== Test saving variable content
zcl_os_ajob_watch=>trace( )->variable(
name = ‘COUNTRY’
var = structure_demo-country ).
” ==== Test saving internal table content
SELECT FROM i_country
FIELDS *
INTO TABLE (itab_demo).
zcl_os_ajob_watch=>trace( )->itab(
name = ‘INTERNAL_TABLE_DEMO’
var = itab_demo ).
zcl_os_ajob_watch=>trace( )->persist_log( abap_true ).
ENDMETHOD.
Final Notes
My goal with this article was to share my journey in working with application jobs and offer ideas to improve the development process. Excellent tutorials and sample code are already available, and I’ve included links to some that I found useful, so I aimed to go beyond creating another how-to guide. My ABAP source code and Fiori app are both public on github.
I would be glad to hear any suggestions or feedback from readers!
Links and Tutorials:
Official SAP Help: https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/application-jobsSoftware Heroes introduction series of articles for application jobs: https://software-heroes.com/en/blog/abap-cloud-jobs-enShort how-to video: https://www.youtube.com/watch?v=YQlufhU5xXU GitHub sample code: https://github.com/SAP-samples/abap-platform-application-jobs
IntroductionClassic batch jobs on the ABAP Application Server for S/4HANA Cloud and the BTP ABAP Environment have been replaced by application jobs. From what I could research myself, this new framework builds on the existing classical batch job functionality adding a modern layer on top and also some extensions. For ABAP developers working in cloud systems, automating tasks and setting up periodic maintenance jobs remains a valid requirement. However, the new application job framework introduces some challenges that I, too, encountered.In this article, I’ll briefly summarize the framework, share the issues I faced while working with application jobs, and explain how I resolved them. For those interested in learning more, I’ve included links to introductory tutorials and GitHub repositories. ABAP Jobs – Classic vs. NewI have tried to summarize the main features of the classic and new way to work with jobs. The most significant change, I believe, is the introduction of two new ABAP artifacts: catalog and template. Using them is straightforward. An ABAP class containing the job logic is developed and linked to a catalog. This catalog is then linked to a template, and finally the template is used for scheduling the job manually or programmatically. Catalogs also provide exit points to perform checks, set up value help for parameters, or send job notifications. The template stores input values for each application job.Figure 1 – Classic vs. new Challenges in Working with Application Jobs and Possible SolutionsLike other aspects in the ABAP Cloud, working with the new application job framework comes with some obstacles, but these are not unsolvable.Adding visible application log to the application jobThis is the simplest issue to solve and came up because I initially overlooked a parameter in the log-saving method. In the method save_log() of class CL_BALI_LOG_DB there is a parameter called assign_to_current_appl_job, which when set to true automatically links the log created during the run of an application job to the monitoring of the job in the Fiori app “Application Jobs”, making it visible there.Figure 2 – How to link application log to an application jobFigure 3 – Application log for an executed job Scheduling a background job with a technical userScheduling application jobs programmatically is possible with cl_apj_rt_api=>schedule_job(), but not all user types can do this. If you need a technical user to schedule jobs to avoid dialog work process timeouts, it’s not currently allowed in ABAP Cloud (as of November 2024).To work around this, you can create an asynchronous start mechanism::Instead of scheduling the job directly, the technical user adds an entry in a custom Z* table, with all the required information about the job to be started;A separate periodic job (running every one minute, 10 minutes or any other interval depending on your needs), launched by a normal business user reads the above mentioned custom Z* table and starts immediately the job. This way, your job is effectively run without requiring a direct start from the technical user Preventing multiple instances of a job from running simultaneouslyA common requirement for periodic jobs is to ensure only one instance runs at a time. While in the on-premise ABAP server you could use report RSBTONEJOB2 to prevent overlap, this isn’t available in the ABAP cloud environment. However, with some code, you can implement a similar solution (source code is in the GitHub link provided):First you identify the data about the current running job with: cl_apj_rt_api=>get_job_runtime_info( );Then you check for other jobs running from the same catalog with: cl_apj_rt_api=>find_jobs_with_jce( catalog );If another instance is running, stop the current instance to prevent overlap. Debugging an application jobOne limitation of the new framework is the inability to debug jobs. In the classic SM37 transaction, you could use the jdbg command to debug a background job. Currently, the new framework lacks this capability to the best of my knowledge.Initially, I tried using log points to log variable contents in the background work process, but this approach failed because ABAP Cloud doesn’t allow LOG-POINT statements. Additionally, while you can add dynamic log points in Eclipse, this requires authorization for object S_DYNLGPTS, which isn’t API-released and therefore couldn’t be added to my role.To overcome this, I created a custom variable-tracking tool. It stores variable contents in a custom table at runtime and displays them in a simple Fiori app. By adding just one line of code, developers can trace variable content at runtime:zcl_os_ajob_watch=>trace( )->itab( name = ‘INTERNAL_TABLE_DEMO’ var = itab_demo ).zcl_os_ajob_watch=>trace( )->structure( name = ‘STRUCTURE_DEMO’ var = structure_demo ).The Fiori app shows the stored variable data per job run, with options to delete recordings as needed:Figure 4 – Visualizing variable content saved at runtime Custom Tools for a Smoother Development ProcessTo tackle all these challenges mentioned above, I built custom small tools with the following principles in mind:They should never interfere with job execution (exceptions are caught without propagation).They should use method chaining, inspired by the XCO Library, for easy implementation and removal when no longer needed.They should have short, meaningful descriptions and keywords for easy usage without additional documentation.Using these tools, the job class implementation might look like this demo code below: METHOD if_apj_rt_exec_object~execute.
” This is a DEMO job execution – it will execute just some dummy checks
” Set from the beginning what object/subobject and external ID you want to use for logging
zcl_os_job_log=>set_info_for_current_session(
obj = appl_log_job-obj “‘ZOS_DEMO_JOB’
sobj = appl_log_job-subobj “‘JOB_LOG’
xid = CONV #( zcl_os_appl_job_util=>get_current_job_name( ) ) ).
” One demo success log
zcl_os_job_log=>log( )->add_message(
severity = if_bali_constants=>c_severity_information
id = appl_log_job-msg_class
number = 002 )->save_log( ). ” This is a demo success message.
” === First I want to make sure that this job is not running already
IF zcl_os_appl_job_util=>is_job_already_running( info_job-catalog ) = abap_true.
zcl_os_job_log=>log( )->add_free_text(
text = |An instance of this job is already running. Will be SKIPPED!|
severity = if_bali_constants=>c_severity_warning )->save_log( ).
RETURN.
ENDIF.
” One demo error log
zcl_os_job_log=>log( )->add_message(
severity = if_bali_constants=>c_severity_error
id = appl_log_job-msg_class
number = 001 )->save_log( ). ” This is a demo error message.
” ==== Test saving structure content
SELECT SINGLE FROM i_country
FIELDS *
WHERE country = ‘RO’
INTO (structure_demo).
zcl_os_ajob_watch=>trace( )->structure(
name = ‘STRUCTURE_DEMO’
var = structure_demo ).
” ==== Test saving variable content
zcl_os_ajob_watch=>trace( )->variable(
name = ‘COUNTRY’
var = structure_demo-country ).
” ==== Test saving internal table content
SELECT FROM i_country
FIELDS *
INTO TABLE (itab_demo).
zcl_os_ajob_watch=>trace( )->itab(
name = ‘INTERNAL_TABLE_DEMO’
var = itab_demo ).
zcl_os_ajob_watch=>trace( )->persist_log( abap_true ).
ENDMETHOD. Final NotesMy goal with this article was to share my journey in working with application jobs and offer ideas to improve the development process. Excellent tutorials and sample code are already available, and I’ve included links to some that I found useful, so I aimed to go beyond creating another how-to guide. My ABAP source code and Fiori app are both public on github.I would be glad to hear any suggestions or feedback from readers!Links and Tutorials:Official SAP Help: https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/application-jobsSoftware Heroes introduction series of articles for application jobs: https://software-heroes.com/en/blog/abap-cloud-jobs-enShort how-to video: https://www.youtube.com/watch?v=YQlufhU5xXU GitHub sample code: https://github.com/SAP-samples/abap-platform-application-jobs Read More Application Development Blog Posts articles
#SAP