Search code examples
jsr352java-batch

JSR-352 Java Batch: why does JobListener.afterJob() always get batch status STARTED?


I'm a Java Batch newbie. I deployed a simple batch job that includes a JobListener on WebSphere Liberty 17.0.0.4 (note that I'm using IBM's JSR-352 implementation, not Spring Batch). The batch job itself runs as expected: it reads an input file, does a simple data transformation, and writes to a DB. But in the JobListener's afterJob() method for a successful execution, I see a batch status of STARTED and an exit status of null. (These are the same values I see logged in the beforeJob() method). I expected afterJob() to see a status of COMPLETED unless there were exceptions.

The execution id logged by afterJob() is the same value returned by JobOperator.start() when I kick off the job, so I know I'm getting the status of the correct job execution.

I couldn't find any examples of a JobListener that fetches a batch status, so there's probably a simple error in my JSL, or I'm fetching the batch status incorrectly. Or do I need to explicitly set a status somewhere in the implementation of the step? I'd appreciate any pointers to the correct technique for setting and getting a job execution's final batch status and exit status.

Here's the JSL:

<job ...>
    <properties>...</properties>

    <listeners>
        <listener ref="jobCompletionNotificationListener"/>
    </listeners>

    <flow id="flow1">
        <step id="step1">...</step>
    </flow>
</job>

Here's the listener's definition in batch.xml:

<ref id="jobCompletionNotificationListener"
     class="com.llbean.batch.translatepersonnames.jobs.JobCompletedListener"/>    

Here's the JobListener implementation:

@Dependent
@Named("jobCompletedListener")
public class JobCompletedListener implements JobListener {
    ...
    @Inject
    private JobContext jobContext;

    @Override
    public void afterJob() {
        long executionId = jobContext.getExecutionId();
        JobExecution jobExecution = BatchRuntime.getJobOperator().getJobExecution(executionId);
        BatchStatus batchStatus = jobExecution.getBatchStatus();
        String exitStatus = jobExecution.getExitStatus();
        logger.info("afterJob(): Job id " + executionId + " batch status = " + batchStatus + 
                ", exit status = " + exitStatus);
        ...
    }
}

I tried adding <end on="*" exit-status="COMPLETED"/> to the <job> and <flow> in the JSL, but that had no effect or resulted in a status of FAILED.


Solution

  • Good question. Let me tack on a couple points to @cheng's answer.

    First, to understand why we implemented it this way, consider the case where the JobListener throws an exception. Should that fail the job? In Liberty we decided it should. But if the job already had a COMPLETED status, well, that would imply that it was... completed, and shouldn't be able to fail at that point.

    So afterJob() is really more like "end of job" (or you could think of it as "after job steps").

    Second, one reason to even ask this question is because you want to know, in the afterJob() method, whether the job executed successfully or not.

    Well, in the Liberty implementation at least (which I work on, for IBM), you can indeed see this. A previous failure will have set the BatchStatus to FAILED while a successful (this far) execution will have its status still at STARTED.

    (For what it's worth, this was an area we realized could use more attention and standardization a bit late in the 1.0 spec effort, and I hope we can address more in the future.)

    If it helps and you're interested, you can see the basic logic in the flow leading up to and including the WorkUnitThreadControllerImpl.endOfWorkUnit call here.