Skip to content

No way to control execution order of TaskBatchExecutionListener #818

@tomgeraghty3

Description

@tomgeraghty3

Context

  • A Spring Batch Job can have multiple JobExecutionListeners associated with it. This is done via the listener() method in JobBuilderFactory
  • When using Spring Batch (via @EnableBatchProcessing) and Spring Cloud Task (via @EnableTask) an auto-configuration bean is instantiated: TaskBatchAutoConfiguration.
  • The TaskBatchAutoConfiguration creates a TaskBatchExecutionListener (which is a JobExecutionListener) that associates the ID of the Spring Batch Job with the ID of the Spring Cloud Task. This also registers it to the Job automatically.
  • When the Job starts, all the registered JobExecutionListeners are executed in the order according to:
    • Any listeners with the @Order interface
    • The order the listeners are registered in

The issue

  • There is no apparent way to control when TaskBatchExecutionListener is executed. It does not have an @Order and is always registered after the listeners manually registered through listener i.e. means it always gets executed last
  • If a listener that is executed before the TaskBatchExecutionListener throws an exception then the TaskBatchExecutionListener is never executed and the association between Job and Task is never made
  • Spring Cloud Data Flow then produces an error when clicking on "Job Executions" page because that expected association is missing

Desired behaviour:

A way to make sure the TaskBatchExecutionListener is always executed first

Attempts to solve

  • AbstractJob has a CompositeJobExecutionListener and a method registerJobExecutionListener() which delegates to the register() method in the Composite listener.
  • The CompositeJobExecutionListener is private in AbstractJob and there is no way to override to instantiate a different CompositeJobExecutionListener (i.e. a potential solution would be to create a subclass that performs the desired sorting of the TaskBatchExecutionListener always being first)
  • Although nasty, you could, via reflection, change the instance of CompositeJobExecutionListener to a subclass but then the listener itself has a private field named listeners which is an OrderedComposite. This class has default visibility so is not immediately subclass-able either

Workaround

  • Autowire in the TaskBatchExecutionListener to whichever Configuration class contains code to create the Job bean
  • Manually register that listener first
  • In this way, when the auto configuration tries to register the same TaskBatchExecutionListener later, it is ignored because the OrderedComposite ignores duplicates

The workaround works fine but shouldn't the framework maybe be registering that listener first? Or allowing a way to control the Order of that auto-created bean?
I only noticed this because SCDF dashboard stopped working and then I had to manually remove the task execution from the database to get it to continue working.
Maybe there is something obvious I am missing...

I have created a very simple example application to demo this behaviour. See SpringBatchTaskJobExecListenerFailsTest: https://github.com/tomgeraghty3/spring-batch-task-job-listeners

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions