I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

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);

@Scheduled(fixedRate = 2000)
public void launchJob() throws Exception {
    if (enabled.get()) {
        Date date = new Date();
        JobExecution jobExecution = jobLauncher()
          .run(job(), new JobParametersBuilder()
            .addDate("launchDate", date)
            .toJobParameters());
        batchRunCounter.incrementAndGet();
    }
}

// stop, start functions (changing the flag of enabled)

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.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS