0

Idea is not to start job if already same job is running.

JobExplorer is simple injected in class where is scheduled method for running

public class JobClass {

    private final Job job;
    private final JobExplorer jobExplorer;
    private final JobLauncher jobLauncher;

    public JobMain(Job job,
                   JobLauncher jobLauncher,
                   JobExplorer jobExplorer) {
        this.job = job;
        this.jobLauncher = jobLauncher;
        this.jobExplorer = jobExplorer;
    }

and then it is executed

    @Scheduled("0 */5 * ? * *")
    public void startJob() {
        JobParameters jobParameters = new JobParametersBuilder()
                    .addString("jobName", String.valueOf(instant.toEpochMilli()))
                    .toJobParameters();
        jobLauncher.run(job, jobParameters);
    }

This is not solution because if JVM stopped while job is running this will be same as current job running:

jobExplorer.findRunningJobExecutions("jobName")

It will find all jobs with exitCode ExitStatus.UNKNOWN.

There is 3 solutions as I see it:

Solution 1:

stop previous running not finished jobs and run new job PROS: everything is clean, just one property

CONT: loosing current execution of current job

@Scheduled("0 */5 * ? * *")
public void startJob() {
    (JobExecution jobExecution: jobExplorer.findRunningJobExecutions("jobName")) jobExecution.stop();
    ...
}

Solution 2

Calculate time between latest running job like it is here described and if it is any do not start new job: https://stackoverflow.com/a/23218986/1182625

PROS: everything is clean

CONT: have to have doubled property (5 *60*1000 and "0 */5 * ? * *")

Set<JobExecution> jobExecutions = jobExplorer.findRunningJobExecutions("jobName");
        if(jobExecutions.size()>1){
            Long currentTime = (new Date()).getTime();
            for(JobExecution execution : jobExecutions) {
                if((currentTime - execution.getStartTime().getTime()) < 5*60*1000) {
                    return;
                } else {
                    execution.stop();
                }
            }

        }

Solution 3

Idea is simple to add static (to share between instances of class) volatile (to share between threads) flag which will indicate is any job currently running PROS: just one property

CONT: needs 2 listeners, and volatile static variable which i don't know how reacted in multi-nodes environment

    private static volatile boolean FINISHED = true;

and then simple add listener and FINISHED modify method:

// reset FINISHED after job is done
     @AfterJob
     public void afterJob() {
         FINISHED = true;
     }

    public void setFinished() {
        this.FINISHED = true;
    }

And simple add:

    @Scheduled("0 */5 * ? * *")
    public void startJob() {
    if(!FINISHED) return;
    FINISHED = false;
    ...
    }

And finally add StepListener

public MyStepListener() {
...
    @AfterStep
    public ExitStatus afterStep(StepExecution stepExecution) {
    if(stepExecution.getExitStatus().getExitCode().equalsIgnoreCase(ExitStatus.FAILED.getExitCode()))  (new JobMain()).setFinished();
    return null;
    }
user1182625
  • 145
  • 2
  • 15

2 Answers2

1

Ok, I think I go to far with something could be KISS.

Keep It Simple & Stupid.

So, to achieve this is simple to put fixedDelay or fixedStringDelay in @Scheduled annotation if you want to use value from properties file.

@Scheduled(initialDelay = 3*60*1000, fixedDelayString ="${job.fixed_delay}")

With this I achieve that I don't have more than 1 instance of same job at same time. I only lose that job start at exactly time (like ad midnight or...)

user1182625
  • 145
  • 2
  • 15
0

Idea is not to start job if already same job is running.

By design, Spring Batch will prevent that. If you try to start the same job instance while it has a running job execution, you will get a JobExecutionAlreadyRunningException.

Mahmoud Ben Hassine
  • 28,519
  • 3
  • 32
  • 50
  • Ok, maybe i wasn't clear. It is new instance of same job. And I don't want new instance of same job if previous job is not finished. – user1182625 Jun 02 '20 at 09:21
  • @Mahmoud, when I launch a job with jobLauncher.run, if I launch the same job before it completes, the first job stops running without finishing and starts again. How can I prevent stopping an unfinished job? – igarciadev Mar 26 '21 at 11:10
  • @IsaacGS I don't see how this is possible, the job launcher does not stop jobs, it only launches them. Please open a separate question with an example and I will take a look. – Mahmoud Ben Hassine Mar 27 '21 at 19:17
  • @Mahmoud [question](https://stackoverflow.com/q/66851786/5668022) Thanks in advance. – igarciadev Mar 29 '21 at 09:34