Search code examples
javaspring-bootspring-batch

how do I use JobLauncherSynchronizer in spring admin


There is an @Aspect that ensures that jobs in spring boot are not re-entrant. It is implemented in JobLauncherSynchronizer.java and documented here. It is in the spring batch admin artifact.

My Job is created through a @Bean as follows:

@Bean
@Qualifier("synchronisationLauncher")
public JobLauncher synchronisationLauncher() throws Exception {
    SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
    jobLauncher.setJobRepository(jobRepository);
    jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
    jobLauncher.afterPropertiesSet();
    return jobLauncher;
}

My question:
how do I integrate the @Aspect into my JobLauncher?

I naively tried to new it up, and set the properties in the method above, not surprisingly that didn't work very well. I was able to launch multiple instances.

My launch code is:

// dependencies from constructor.
// the launcher is qualified to 'synchronisationLauncher'
private JobLauncher launcher;
private Job mergeJob;
private JobQueryHelper jobQueryHelper;

@PostMapping("uri")
public ResponseEntity<APIResponse> SynchroniseRetailers()
        throws JobParametersInvalidException,
               JobExecutionAlreadyRunningException,
               JobRestartException,
               JobInstanceAlreadyCompleteException {

    JobParametersBuilder builder = new JobParametersBuilder()
            .addLong("run.id", jobQueryHelper.findLastBatchExecutionIndex() + 1);
    launcher.run(mergeJob, builder.toJobParameters());
    return buildEmptyResponse();
}

Solution

  • What you can do is override the SimpleJobLauncher#run method and add the double check before running the job:

    @Bean
    public JobLauncher jobLauncher(JobRepository jobRepository, JobExplorer jobExplorer) {
        SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher() {
            @Override
            public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException {
                Set<JobExecution> running = jobExplorer.findRunningJobExecutions(job.getName());
                if (!running.isEmpty()) {
                    throw new JobExecutionAlreadyRunningException("An instance of this job is already active: " + job.getName());
                }
                return super.run(job, jobParameters);
            }
        };
        simpleJobLauncher.setJobRepository(jobRepository);
        simpleJobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
        simpleJobLauncher.afterPropertiesSet();
        return simpleJobLauncher;
    }
    

    Note this is similar to the code in the aspect you shared but without needing any kind of AOP. As I mentioned in the comment, this check is already done in super.run(job, jobParameters), so we are only adding a double check here.