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.