Expected Behavior:

A JobParametersIncrementer is expected to modify the given JobParameters to provide a modified set of JobParameters. If the new set of JobParameters is unique for the Job then a new JobInstance will be created.

Actual Behavior:

Before the JobParametersIncrementer is executed, a JobInstance is found in the "if" statement on line 206 in method getNextJobParameters(Job job, JobParameters jobParameters) of JobLauncherApplicationRunner.

    private JobParameters getNextJobParameters(Job job, JobParameters jobParameters) {
        if (this.jobRepository != null && this.jobRepository.isJobInstanceExists(job.getName(), jobParameters)) {
            return getNextJobParametersForExisting(job, jobParameters);
        }
        if (job.getJobParametersIncrementer() == null) {
            return jobParameters;
        }
        JobParameters nextParameters = new JobParametersBuilder(jobParameters, this.jobExplorer)
                .getNextJobParameters(job).toJobParameters();
        return merge(nextParameters, jobParameters);
    }

The method getNextJobParametersForExisting(Job job, JobParameters jobParameters) is then executed which in the case of a 'COMPLETED' JobExecution will return the original JobParameters object.

    private JobParameters getNextJobParametersForExisting(Job job, JobParameters jobParameters) {
        JobExecution lastExecution = this.jobRepository.getLastJobExecution(job.getName(), jobParameters);
        if (isStoppedOrFailed(lastExecution) && job.isRestartable()) {
            JobParameters previousIdentifyingParameters = getGetIdentifying(lastExecution.getJobParameters());
            return merge(previousIdentifyingParameters, jobParameters);
        }
        return jobParameters;
    }

Use Case

Restarting a 'COMPLETED' job using a JobParameterIncrementer. Below is the JobParameterIncrementer expected to provide a unique set of JobParameters for each execution.

package util;

import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersIncrementer;

public class ForceRestart implements JobParametersIncrementer {

    private static final long JOB_PARAMETER_MAXIMUM = 1000000;

    @Override
    public JobParameters getNext(JobParameters params) {

        Long random = (long) (Math.random() * JOB_PARAMETER_MAXIMUM);

        return new JobParametersBuilder(params).addLong("random", random).toJobParameters();
    }
}

Version: 2.2.6.RELEASE

Comment From: wilkinsona

@benas Can we have some of your expertise here please? I believe both JobLauncherCommandLineRunner and JobLauncherApplicationRunner have the same behaviour at the moment.

Comment From: fmbenhassine

Expected Behavior: A JobParametersIncrementer is expected to modify the given JobParameters to provide a modified set of JobParameters

@touchrock Where did you read that expected behavior? The incrementer does not increment the given parameters, it increments the parameters of the previous instance in the sequence. This is explained in the reference documentation and in the Javadocs.

Use Case Restarting a 'COMPLETED' job using a JobParameterIncrementer

In Spring Batch, it is not possible to restart a completed job instance by design (if you attempt to do so you will get a JobInstanceAlreadyCompleteException), you can only restart a failed or stopped instance, and this is true with or without a JobParametersIncrementer. So trying to create an incrementer named ForceRestart that adds a random number as a parameter will not "force a restart", it will only create a new job instance that is different from the previous one in the sequence.

The current behavior is correct: the job runner first checks the existence of a job instance with the given parameters and if a completed instance is present then it returns the same parameters which will make the job launcher throw a JobInstanceAlreadyCompleteException as designed.

Comment From: wilkinsona

Thanks very much, @benas.