1. Overview

In this tutorial, we’ll investigate and compare different ways to trigger and stop a scheduled Spring Batch job for any required business cases.

If you need introductions about Spring Batch and Scheduler, please refer to Spring-Batch and Spring-Scheduler articles.

2. Trigger a Scheduled Spring Batch Job

Firstly, we have a class SpringBatchScheduler to configure scheduling and batch job. A method launchJob() will be registered as a scheduled task.

Furtherly, to trigger the scheduled Spring Batch job in the most intuitive way, let’s add a conditional flag to fire the job only when the flag is set to true:

private AtomicBoolean enabled = new AtomicBoolean(true);

private AtomicInteger batchRunCounter = new AtomicInteger(0);

@Autowired
private JobLauncher jobLauncher;

@Autowired
private JobRepository jobRepository;

@Autowired
private PlatformTransactionManager transactionManager;

@Scheduled(fixedRate = 2000)
public void launchJob() throws Exception {
    Date date = new Date();
    logger.debug("scheduler starts at " + date);
    if (enabled.get()) {
       JobExecution jobExecution = jobLauncher.run(job(jobRepository, transactionManager), new JobParametersBuilder().addDate("launchDate", date)
                  .toJobParameters());
       batchRunCounter.incrementAndGet();
       logger.debug("Batch job ends with status as " + jobExecution.getStatus());
    }
    logger.debug("scheduler ends ");
}

The variable batchRunCounter will be used in integration tests to verify if the batch job has been stopped.

3. Stop a Scheduled Spring Batch Job

With above conditional flag, we’re able to trigger the scheduled Spring Batch job with the scheduled task alive.

If we don’t need to resume the job, then we can actually stop the scheduled task to save resources.

Let’s take a look at two options in the next two subsections.

3.1. Using Scheduler Post Processor

Since we’re scheduling a method by using @Scheduled annotation, a bean post processor ScheduledAnnotationBeanPostProcessor would’ve been registered first.

We can explicitly call the postProcessBeforeDestruction() to destroy the given scheduled bean:

@Test
public void stopJobSchedulerWhenSchedulerDestroyed() throws Exception {
    ScheduledAnnotationBeanPostProcessor bean = context
      .getBean(ScheduledAnnotationBeanPostProcessor.class);
    SpringBatchScheduler schedulerBean = context
      .getBean(SpringBatchScheduler.class);
    await().untilAsserted(() -> Assert.assertEquals(
      2, 
      schedulerBean.getBatchRunCounter().get()));
    bean.postProcessBeforeDestruction(
      schedulerBean, "SpringBatchScheduler");
    await().atLeast(3, SECONDS);

    Assert.assertEquals(
      2, 
      schedulerBean.getBatchRunCounter().get());
}

Considering multiple schedulers, it’s better to keep one scheduler in its own class, so we can stop specific scheduler as needed.

3.2. Canceling the Scheduled Future

Another way to stop the scheduler would be manually canceling its Future.

Here’s a custom task scheduler for capturing Future map:

@Bean
public TaskScheduler poolScheduler() {
    return new CustomTaskScheduler();
}

private class CustomTaskScheduler 
  extends ThreadPoolTaskScheduler {

    //

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(
      Runnable task, long period) {
        ScheduledFuture<?> future = super
          .scheduleAtFixedRate(task, period);

        ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
        scheduledTasks.put(runnable.getTarget(), future);

        return future;
    }
}

Then we iterate the Future map and cancel the Future for our batch job scheduler:

public void cancelFutureSchedulerTasks() {
    scheduledTasks.forEach((k, v) -> {
        if (k instanceof SpringBatchScheduler) {
            v.cancel(false);
        }
    });
}

In the cases with multiple scheduler tasks, then we can maintain the Future map inside of the custom scheduler pool but cancel the corresponding scheduled Future based on scheduler class.

4. Conclusion

In this quick article, we tried three different ways to trigger or stop a scheduled Spring Batch job.

When we need to restart the batch job, using a conditional flag to manage job running would be a flexible solution. Otherwise, we can follow the other two options to stop the scheduler completely.

As usual, all the code samples used in the article are available over on GitHub.