15

I have one spring batch job which can be kicked of by rest URL. I want to make sure only one job instance is allowed to run. and if another instance already running then don't start another. even if the parameters are different.

I searched and found nothing out of box solution. thinking of extending SimpleJobLauncher. to check if any instance of the job running or not.

sandeep
  • 996
  • 2
  • 11
  • 22

4 Answers4

7

You could try to intercept the job execution, implementing the JobExecutionListener interface:

public class MyJobExecutionListener extends JobExecutionListener {

    //active JobExecution, used as a lock.
    private JobExecution _active;

    public void beforeJob(JobExecution jobExecution) {
        //create a lock
        synchronized(jobExecution) {
            if(_active!=null && _active.isRunning()) {
                jobExecution.stop();
            } else {
                _active=jobExecution;
            }
        }
    }

    public void afterJob(JobExecution jobExecution) {
          //release the lock
          synchronized(jobExecution) {
              if(jobExecution==_active) {
                _active=null; 
              }
          }
    }
}

And then, inject to the Job definition:

<job id="myJobConfig">
    <listeners>
        <listener ref="myListener"/>
    </listeners>
</job>
Slava Vedenin
  • 58,326
  • 13
  • 40
  • 59
Tomas Narros
  • 13,390
  • 2
  • 40
  • 56
  • 1
    Thanks for this solution I need to try this and will update if it works for me and meet my all requirements. – sandeep Apr 21 '14 at 09:34
  • 1
    What happens if you shut down the server while a job is running? If you restart the server it will be still marked as started but is actually broken. Wouldn't this solution then not start an instance of this job anymore? – kukudas Nov 29 '16 at 14:11
  • You are a lifesaver, Tomas. Thanks a lot. I used java config and it worked like a charm jobBuilderFactory.get("jobName").listener(new BatchJobExecutionListener()).start... – Harshit Dec 16 '21 at 07:43
6

I solved this by creating an JobExecutionListner and with the help of JobExplorer I checked if any other instance is running if running then stop current job.I created listener so that it can be plugged in to any job that requires this kind of scenario.

Set<JobExecution> jobExecutions = ((SimpleJobExplorer) jobExplorer.getObject()).findRunningJobExecutions(jobExecution.getJobInstance().getJobName());
            if(jobExecutions.size()>1){
                Long currentTime = (new Date()).getTime();
                for(JobExecution execution : jobExecutions ){
                    if(execution.getJobInstance().getId().compareTo(jobExecution.getJobInstance().getId())!=0 && (currentTime - execution.getStartTime().getTime()) <lockOverideTime){
                        jobExecution.stop();
                        throw new IllegalStateException("Another instance of the job running job name : " +jobExecution.getJobInstance().getJobName() );

                    }
                }

            }
sandeep
  • 996
  • 2
  • 11
  • 22
3

Or, in response to REST URL, check using JobExplorer if your job is running using job's specifics business rules

Luca Basso Ricci
  • 17,829
  • 2
  • 47
  • 69
0

I think a simple method like the following might do the trick:

@Autowire
private JobExplorer jobExplorer;

private boolean isJobRunning(Job job) {
    Set<JobExecution> jobExecutions = jobExplorer.findRunningJobExecutions(job.getName());
    return !jobExecutions.isEmpty();
}

Then, prior to executing your job make the check:

private void executeJob(Job job, @Nonnull JobParameters params) {
    if (isJobRunning(job)) {
        return;
    }

    try {
        jobLauncher.run(job, params);
    } catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException | JobParametersInvalidException e) {
        log.error("could not run job " + jobIdentifier, e);
    }
}
mtsahakis
  • 793
  • 1
  • 8
  • 18
  • 3
    findRunningJobExecutions() basically returns job executions without an endTime. If you have older executions that didn't finish correctly, this won't work. – Ivo van der Veeken Jan 14 '19 at 13:35
  • @IvovanderVeeken little late to party. Is there any solution to overcome this caveat? – Muhammed Thabjeel Mar 20 '23 at 06:02
  • @MuhammedThabjeel It's been ages since I've used spring batch so I don't know the specifics anymore, but you usually want to filter on some kind of status. Instead of all jobs without an endTime, you want those without an endTime and without status running, errored, stopped, or whatever you need. Looks like you only want the jobs with status "running" in this case. – Ivo van der Veeken Mar 21 '23 at 10:52
  • Like I said, I don't know the spring batch specifics anymore and I'm assuming jobs that failed previously don't keep the "running" status. If they do, there's not much to filter on and you'll have to take a look at your exception handling to make sure they get the "error" status. – Ivo van der Veeken Mar 21 '23 at 10:57