spring-batch
spring-batch copied to clipboard
Make JobStep stoppable
We use JobStep-based implementation to call another job from a job, but unfortunately it doesn't support step.stop() at the moment when parent job stop request arrives via JobOperator.stop().
There are two moments which require adjustments:
- SimpleJobOperator
- JobStep along with nested job execution tracking
SimpleJobOperator: It needs additional check in its stop() method as in:
@Override
public boolean stop(long executionId) throws NoSuchJobExecutionException, JobExecutionNotRunningException {
...
if (tasklet instanceof StoppableTasklet) {
StepSynchronizationManager.register(stepExecution);
((StoppableTasklet)tasklet).stop(stepExecution);
StepSynchronizationManager.release();
**} else if (step instanceof JobStep) {
StepSynchronizationManager.register(stepExecution);
((JobStep) step).stop(stepExecution, this);
StepSynchronizationManager.release();
}**
...
JobStep: Currently, JobStep doesn't track its nested spawned job execution which would need to be stopped as per request above: ((JobStep) step).stop(stepExecution, this).
We achieve this via JobExecutionListener.beforeJob() and save just spawned job execution ID into parent job's step execution context (that's why, we needed stepExecution parameter above - to find nested job execution ID to be stopped by JobOperator).
JobStep stop() method could roughly look like this:
public void step(StepExecution stepExecution, JobOperator jobOperator) {
// find spawned job execution from stepExecution (e.g., from its execution context)
...
// This is preventive validation, otherwise, stop() will fail if job has been already completed
// Validation below is taken from Spring Batch JobOperator.stop()
JobExecution jobExecution = jobOperator.findExecutionById(childJobExecutionId);
BatchStatus status = jobExecution.getStatus();
if (!(status == BatchStatus.STARTED || status == BatchStatus.STARTING)) {
// Job is not running
LOGGER.warn("{} child job execution execution is not running - it cannot be stopped", childJobExecutionId);
} else {
jobOperator.stop(childJobExecutionId);
}
}
If similar behavior to track nested job execution ID is hard to implement in generic way, at least, the following default method could be introduced in JobStep:
default protected Long getChildExecutionId(StepExecution stepExecution) {
return null;
}
In such a way JobStep could be inherited and the method defined accordingly.
Thank you in advance!