Partner – Microsoft – NPI (cat= Spring)
announcement - icon

Azure Spring Apps is a fully managed service from Microsoft (built in collaboration with VMware), focused on building and deploying Spring Boot applications on Azure Cloud without worrying about Kubernetes.

And, the Enterprise plan comes with some interesting features, such as commercial Spring runtime support, a 99.95% SLA and some deep discounts (up to 47%) when you are ready for production.

>> Learn more and deploy your first Spring Boot app to Azure.

You can also ask questions and leave feedback on the Azure Spring Apps GitHub page.

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.

Course – LS (cat=Spring)

Get started with Spring and Spring Boot, through the Learn Spring course:

>> THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.