{{announcement.body}}
{{announcement.title}}

Dynamically Schedule the Same Task with Multiple Cron Expression Using Spring Boot

DZone 's Guide to

Dynamically Schedule the Same Task with Multiple Cron Expression Using Spring Boot

Learn how to schedule a task with multiple cron expressions with Spring Boot and schedule them using ScheduledTaskRegistrar.

· Java Zone ·
Free Resource

We have seen how to schedule a task within Spring or Spring Boot using a cron expression, with fixed delays. In this in this tutorial, we will see how to schedule a task with multiple cron expressions and change the cron expression on the fly without restarting the server.

Creating a cron trigger task with Spring is pretty easy by adding a @Scheduled annotation to any method and adding @EnableScheduling in any configuration file. But here we use  org.springframework.scheduling.annotation.SchedulingConfigurer  interface . SchedulingConfigurer class has one method,configureTasks(ScheduledTaskRegistrar taskRegistrar) which will be called at the time of bootstrap and register the tasks.

Here, with the help of ScheduledTaskRegistrar, we schedule the task to run at particular time intervals or with fixed rate delays. 

First, we need to implement the SchedulingConfigurer interface where we need to schedule the cron tasks. Refer to the below code to see how to do that.

Java
 




xxxxxxxxxx
1
28
9


1
@Configuration
2
@EnableScheduling
3
public class SchedulerConfig implements SchedulingConfigurer {
4
  @Override
5
  public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
6
 
          
7
    }
8
}


ScheduledTaskRegistrar has multiple override methods to schedule the task. Here I am going to use the   addTriggerTask(Runnable task, Trigger trigger)  which will take Runnable task which we want to schedule and a Trigger at which intervals the task has to be triggered.

ScheduledTaskRegistrar internally implements the InitializingBean so upon creating the bean, it will call the overriden afterProperties method which will start scheduling the added triggered tasks using ScheduledExecutorService and TaskScheduler. See how to add a trigger task using ScheduledTaskRegistrar below.

Java
 




x




1
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
2
 
          
3
        Runnable runnable = () -> System.out.println("Trigger task executed at " + new Date());
4
 
          
5
  Trigger trigger = new Trigger() {
6
    @Override
7
 
          
8
            public Date nextExecutionTime(TriggerContext triggerContext) {
9
 
          
10
                CronTrigger crontrigger = new CronTrigger("0 0/1 * * * *");
11
 
          
12
                return crontrigger.nextExecutionTime(triggerContext);
13
 
          
14
            }
15
 
          
16
        };
17
 
          
18
        scheduledTaskRegistrar.addTriggerTask(runnable, trigger);
19
 
          
20
    }
21
 
          


With the above code, the runnable task executes at every one minute. 

Now, let's see how to schedule the same task with two or more cron expressions. Here I am taking the list of cron expressions separated with the pipe symbol. In this code, I am running a loop inside the configureTasks to add the trigger for each cron in the list of cron expressions.

Java
 




xxxxxxxxxx
1
29


1
String cronsExpressions = "0 0/1 * * * * | 0 0/5 * * * * | 0 0/10 * * * *";
2
 
          
3
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { 
4
 
          
5
// Split the cronExpression with pipe and for each expression add the same task.
6
    Stream.of(StringUtils.split(cronsExpressions, "|")).forEache(cronExpression ->
7
 
          
8
         Runnable runnable = () -> System.out.println("Trigger task executed at " + new Date());
9
 
          
10
                      Trigger trigger = new Trigger() {
11
 
          
12
                        @Override
13
 
          
14
                        public Date nextExecutionTime(TriggerContext triggerContext) {
15
 
          
16
                            CronTrigger crontrigger = new CronTrigger(cronExpression);
17
 
          
18
                            return crontrigger.nextExecutionTime(triggerContext);
19
 
          
20
                        }
21
 
          
22
                    };
23
 
          
24
               scheduledTaskRegistrar.addTriggerTask(runnable, trigger);
25
 
          
26
}
27
 
          
28
}
29
 
          



With the above code, the runnable task excutes every minute or every 5 or 10 minutes all the time.

Now, to change the task to run at every 2, 7, or 9 minutes all the time and the stop the triggers added for evey 1, 5, or 10 minutes but without having to restart my server. Here the configureTasks will be called during the bootstrap only, but how do we do that now by stopping the existing triggers and creating new trigger task with new cron expressions?

First I will maintain a database property where I will have the cron expression separated by the pipe symbol so that I can change the cron expression on the fly without changing the server.

During the every trigger time I will check the cron expression modified by pulling the cron from data base.

If there is a change in the cron expression, it will stop all the scheduled task and rerun the  configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar)  concurrently again for each of the new cron expression.

Then call the afterProperties  method of  ScheduledTaskRegistrar  to schedule the task with new cron expressions. 

Here when we destroy the existing triggered job it will shutdown the ScheduledExecutorService also, so it is our responsibility to create new ExecutorService and give to  ScheduledTaskRegistrar .

Here is the complete example: 

Java
 




x
1
41


1
@Configuration
2
@EnableScheduling
3
public class SchedulerConfig implements SchedulingConfigurer, DisposableBean {
4
 
          
5
    protected String cronExpressions;
6
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
7
    configDataService // to store the cronexpression in data base so that we can change on the fly when server is running.
8
 
          
9
    @Override
10
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
11
        cronExpressions = configDataService.getPropertyValue(CRON_EXPRESSIONS);
12
        Stream.of(StringUtils.split(cronExpressions, "|")).forEach(cron -> {
13
            Runnable runnableTask = () -> System.out.println("Task executed at ->" + new Date());
14
 
          
15
            Trigger trigger = new Trigger() {
16
                @Override
17
                public Date nextExecutionTime(TriggerContext triggerContext) {
18
                    String newCronExpression = configDataService.getPropertyValue(CRON_EXPRESSIONS);
19
                    if (!StringUtils.equalsAnyIgnoreCase(newCronExpression, cronExpressions)) {
20
                        taskRegistrar.setTriggerTasksList(new ArrayList<TriggerTask>());
21
                        configureTasks(taskRegistrar); // calling recursively.
22
                        taskRegistrar.destroy(); // destroys previously scheduled tasks.
23
                        taskRegistrar.setScheduler(executor);
24
                        taskRegistrar.afterPropertiesSet(); // this will schedule the task with new cron changes.
25
                        return null; // return null when the cron changed so the trigger will stop.
26
                    }
27
                    CronTrigger crontrigger = new CronTrigger(cron);
28
                    return crontrigger.nextExecutionTime(triggerContext);
29
                }
30
            };
31
            taskRegistrar.addTriggerTask(runnable, trigger);
32
        });
33
    }
34
 
          
35
    @Override
36
    public void destroy() throws Exception {
37
        if (executor != null) {
38
            executor.shutdownNow();
39
        }
40
    }
41
}



Always make sure to shut down any executor services that you create.

Please comment here if you see any issues or if you have any suggestions.

Topics:
cron job ,java ,spring boot ,spring boot 2.0 ,spring scheduler

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}