{getToc} $title={Table of Contents}
We need 2 dependencies only for doing this demo project about dynamic cron scheduling tasks with spring boot.
Let's see in the logging. Now cron schedule have trigger in every 10 seconds.
Now, let's check the loggint. That's will be trigger cron at every 15 seconds.
Dynamic Spring Boot Cron Jobs |
Introduction
In this blog, I will do the implementation by creating a new spring boot
project in order to demo how to create the dynamic cron jobs
scheduling
by useing Spring Boot.
Firstly, we'll set up project from start.spring.io to build a spring boot maven project from scratch.
set up spring boot maven project |
We need 2 dependencies only for doing this demo project about dynamic cron scheduling tasks with spring boot.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.4</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>yuthads.blogspot.com</groupId> <artifactId>dynamiccrontasks</artifactId> <version>0.0.1-SNAPSHOT</version> <name>dynamiccrontasks</name> <description>Demo project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
To use cron schedule tasks in Spring Boot projects, you can use cron
expressions and define them in advance in the configuration file. You cannot
dynamically modify the task execution time during project operation, which is
very inflexible.
I'm going to introduces 2 ways in order to create dynamic Spring Boot
cron jobs schedule sasks.
1. Cron expressions
A cron expression
is a string of six to seven fields separated by white space to represent
triggers on the second, minute, hour, day of the month, month, day of the
week, and optionally the year. However, the cron expression in Spring
Scheduler can be as simple as * * * * ? * or as complex as
0 0/5 14,18,3-39,52
? JAN,MAR,SEP MON-FRI 2002-2010.
I'm going to create a schedule timer in one file called:
task-scheduling.ini
scheduling.cron=0/5 * * * * ?
This mean the interval of cron running is in every 5 seconds.
In application.properties file, I have set server port 8080 to run
spring boot application on that port.
server.port=8080
Cron Expression scheduled task execution class:
package yuthads.blogspot.com.dynamiccrontasks.task.scheduling; import java.time.LocalDateTime; import java.util.Date; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.TriggerContext; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.support.CronTrigger; import org.springframework.stereotype.Component; import lombok.Data; import lombok.extern.slf4j.Slf4j; @Data @Slf4j @Component @PropertySource("classpath:/task-scheduling.ini") public class CronExpressionScheduleTask implements SchedulingConfigurer { @Value("${scheduling.cron}") private String cron; @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.addTriggerTask(new Runnable() { @Override public void run() { log.info("Current time: {}", LocalDateTime.now()); } }, new Trigger() { @Override public Date nextExecutionTime(TriggerContext triggerContext) { CronTrigger cronTrigger = new CronTrigger(cron); Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext); return nextExecutionTime; } }); } }
I'm going to create a controller in order to create an end-point, so that the
execution time of the scheduled task can be dynamically modified by calling
that api end-point:
package yuthads.blogspot.com.dynamiccrontasks.controller; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import yuthads.blogspot.com.dynamiccrontasks.task.scheduling.CronExpressionScheduleTask; @RestController @Slf4j @RequiredArgsConstructor @RequestMapping("/scheduling") public class TaskSchedulingCntr { private final CronExpressionScheduleTask taskScheduling; @GetMapping("/update_cron") public ResponseEntity<String> updateCron(String cron) { log.info("Current cron schedule: {}", cron); taskScheduling.setCron(cron); return new ResponseEntity<>("ok",HttpStatus.OK); } }
Before you run the project, don't forget to enable spring scheduling
annotation. Scheduling is not enabled by default. Before adding any
scheduled jobs we need to enable scheduling explicitly by adding the
@enableScheduling annotation:
package yuthads.blogspot.com.dynamiccrontasks;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class DynamiccrontasksApplication {
public static void main(String[] args) {
SpringApplication.run(DynamiccrontasksApplication.class, args);
}
}
Let's running project as Spring Boot App. After we have successfully started
up the project, then we'll get this kind of logging.
y.b.c.d.DynamiccrontasksApplication : The following 1 profile is active: "dev" o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) o.apache.catalina.core.StandardService : Starting service [Tomcat] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.65] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1132 ms o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing y.b.c.d.DynamiccrontasksApplication : Started DynamiccrontasksApplication in 2.155 seconds (JVM running for 3.012) y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:26:20.005919800 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:26:25.004856500 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:26:30.006997200 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:26:35.016202600 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:26:40.015805500 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:26:45.013416600
The schedule time interval would be in every 5 seconds as we have configured
in the task-scheduling.ini file.
Then Let's try to modify cron schedule timer by calling the end-point which we
have already created in the Scheduling Controller class by using postman
tools.
Let's pass in the request parameter cron expression, and modify the scheduled
task to execute once every 10 seconds
We'll set the cron expression to 0/10 * * * * ?.
http://localhost:8080/scheduling/update_cron?cron=0/10 * * * * ? |
Let's see in the logging. Now cron schedule have trigger in every 10 seconds.
y.b.c.d.controller.TaskSchedulingCntr : Current cron schedule: 0/10 * * * * ? y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:34:05.002866500 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:34:10.011560600 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:34:20.008959500 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:34:30.013743100 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:34:40.001236500 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:34:50.016377 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:35:00.004262900 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:35:10.004201 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:35:20.007959100 y.b.c.d.t.s.CronExpressionScheduleTask : Current time: 2022-10-07T14:35:30.010241700
2. Set Dynamic Scheduling using Trigger
In addition to the above method with the help of
cron expressions, there is another trigger, which is different from the CronTrigger trigger.
This trigger can set the cycle interval time at will, unlike the cron
expression, which can only define an interval less than or equal to 59
seconds.
package yuthads.blogspot.com.dynamiccrontasks.task.scheduling; import java.time.LocalDateTime; import java.util.Date; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.TriggerContext; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.support.PeriodicTrigger; import org.springframework.stereotype.Component; import lombok.Data; import lombok.extern.slf4j.Slf4j; @Data @Slf4j @Component public class TriggerScheduleTask implements SchedulingConfigurer { private Long timer = 10000L; @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.addTriggerTask(new Runnable() { @Override public void run() { log.info("Current trigger time: {}", LocalDateTime.now()); } }, new Trigger() { @Override public Date nextExecutionTime(TriggerContext triggerContext) { PeriodicTrigger periodicTrigger = new PeriodicTrigger(timer); Date nextExecutionTime = periodicTrigger.nextExecutionTime(triggerContext); return nextExecutionTime; } }); } }
Let's create an other end-point, so that the execution time of the scheduled
task can be dynamically modified by calling that api end-point:
package yuthads.blogspot.com.dynamiccrontasks.controller; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import yuthads.blogspot.com.dynamiccrontasks.task.scheduling.TriggerScheduleTask; @RestController @Slf4j @RequiredArgsConstructor @RequestMapping("/scheduling") public class TaskSchedulingCntr { private final TriggerScheduleTask triggerScheduleTask; @GetMapping("/update_timer") public ResponseEntity<String> updateTimer(Long timer) { log.info("Current timer schedule: {}", timer); triggerScheduleTask.setTimer(timer); return new ResponseEntity<>("ok",HttpStatus.OK); } }
Let's run project again, and check in the logging after successfully started
up project, then you will get schedule interval will be trigger at every 10
seconds as we have set the timer = 10000L. It's mean 10 seconds.
private Long timer = 10000L;
Logging.
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:48:57.974380700 y.b.c.d.DynamiccrontasksApplication : Started DynamiccrontasksApplication in 2.058 seconds (JVM running for 3.276) y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:49:07.987791 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:49:17.990261400 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:49:27.995728700 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:49:38.003339 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:49:48.011247900 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:49:58.013432900 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:50:08.014068600
Lastly, let's modify the trigger schedule timer dynamically by using the
controller end-point by using postman tools again.
Let's set schedule timer to trigger cron at every 15 seconds.
http://localhost:8080/scheduling/update_timer?timer=15000 |
Now, let's check the loggint. That's will be trigger cron at every 15 seconds.
y.b.c.d.controller.TaskSchedulingCntr : Current timer schedule: 150000 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:55:18.230800 y.b.c.d.controller.TaskSchedulingCntr : Current timer schedule: 15000 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:57:48.242307200 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:58:03.258612600 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:58:18.268559500 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:58:33.282879400 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:58:48.292707200 y.b.c.d.t.s.TriggerScheduleTask : Current trigger time: 2022-10-07T14:59:03.297736800
That's it.
Conclusion
Hope this article was helpful.
Cron runs as a daemon process. This means it only needs to be started once and
it will keep running in the background. This process makes use of crontab to
read the entries of the schedules and kicks off the tasks.