Dynamic Task scheduling with Spring Boot

Ritesh Shergill
4 min readMar 17, 2021

--

How many times have we had the requirement to schedule tasks ahead of time in our projects? Sometimes it seems like a major headache to write the logic to encapsulate the scheduling of a task.

→We might create a Cron job and have it invoke a jar.

→We might try a polling mechanism that polls the system time and when the time to trigger the task is reached, we perform the task. But that would be a very inefficient way to solve this problem given that most operating systems already have a Task Scheduler.

Well worry not, for Spring Boot has the Task Scheduler api that will help us achieve just this — schedule a task from anywhere to run at anytime on our system.

We will specifically be using CronTrigger to trigger the task based on the provided cron expression. Its extremely straightforward and won’t have you wracking your brains to implement your own solution.

Prerequisites:

Java ≥1.8

Any IDE that supports Java programming

Preparation to have your mind blown.

I have created a sample Spring Boot project and am spilling the innards for you to see.

@SpringBootApplication
@EnableScheduling
public class SchedulingappApplication {

public static void main(String[] args) {
SpringApplication.run(SchedulingappApplication.class, args);
}

}

That is the main class and of note should be the @EnableScheduling annotation that allows us to run scheduling within the app.

Then we have a TaskDefinitionBean that implements Runnable and performs the unit of work in its run method.

@Service
public class TaskDefinitionBean implements Runnable {

private TaskDefinition taskDefinition;

@Override
public void run() {
System.out.println("Running action: " + taskDefinition.getActionType());
System.out.println("With Data: " + taskDefinition.getData());
}

public TaskDefinition getTaskDefinition() {
return taskDefinition;
}

public void setTaskDefinition(TaskDefinition taskDefinition) {
this.taskDefinition = taskDefinition;
}
}

In this case we are simply printing the Task Definition to the console as our unit of work. This could be anything from

  • Sending an email
  • Sending a reminder notification
  • Reading data from a database and sending it elsewhere

The possibilities are limitless.

We have a TaskDefinition POJO (or TDO — Task Definition Object) that encapsulates the actual data and task definition.

@Data
public class TaskDefinition {

private String cronExpression;
private String actionType;
private String data;
}

@Data is a lombok annotation to create the getter setters in the generated class file when the code is compiled.

We have the TaskSchedulingService that actually schedules the task to be executed according to its cron expression.

@Service
public class TaskSchedulingService {

@Autowired
private TaskScheduler taskScheduler;

Map<String, ScheduledFuture<?>> jobsMap = new HashMap<>();

public void scheduleATask(String jobId, Runnable tasklet, String cronExpression) {
System.out.println("Scheduling task with job id: " + jobId + " and cron expression: " + cronExpression);
ScheduledFuture<?> scheduledTask = taskScheduler.schedule(tasklet, new CronTrigger(cronExpression, TimeZone.getTimeZone(TimeZone.getDefault().getID())));
jobsMap.put(jobId, scheduledTask);
}

public void removeScheduledTask(String jobId) {
ScheduledFuture<?> scheduledTask = jobsMap.get(jobId);
if(scheduledTask != null) {
scheduledTask.cancel(true);
jobsMap.put(jobId, null);
}
}
}

The scheduleATask method schedules the Cron job based on the provided cron expression. When the trigger time is reached, the tasklet run method will be executed and the provided TaskDefinition will be processed.

You will also notice that we have exposed a jobsmap to store the created tasks and remove them once they are no longer needed. We use a UUID generator to generate random UUIDs to identify the created jobs an then remove them at a later time. But for the purposes of this article, we will only be talking about the scheduling and triggering of the task.

Last but not the least, we have the rest controller to allow us to schedule tasks from anywhere at anytime and for anytime.

@RestController
@RequestMapping(path = "/schedule")
public class JobSchedulingController {

@Autowired
private TaskSchedulingService taskSchedulingService;

@Autowired
private TaskDefinitionBean taskDefinitionBean;

@PostMapping(path="/taskdef", consumes = "application/json", produces="application/json")
public void scheduleATask(@RequestBody TaskDefinition taskDefinition) {
taskDefinitionBean.setTaskDefinition(taskDefinition);
taskSchedulingService.scheduleATask(UuidGenerator.generateUuid(), taskDefinitionBean, taskDefinition.getCronExpression());
}

@GetMapping(path="/remove/{jobid}")
public void removeJob(@PathVariable String jobid) {
taskSchedulingService.removeScheduledTask(jobid);
}
}

Based on the provided TaskDefinition to the /taskdef endpoint, the TaskSchedulingService will queue the task to be executed at a later time.

Next we run the Spring Boot app to start the Task Scheduling Service. We then submit a task through postman.

Postman Post request for task definition submission

I am scheduling the task to be executed at 11.24 pm which you might thing is an oddly weird time to be executing a task but its just the time at which I was running this demo. Yes I sleep late once the midnight oil is burnt.

And we see the result of task submission helpfully printed to the console

Scheduling task with job id: 167a3e89-a86b-4f20–85cc-961251c909ee and cron expression: 0 4 23 * * ?

And at 11.24 pm we see the result of the execution.

Running action: PrintDataTask
With Data: Data to be printed

So ez right?

To summarize Spring makes life so much easier. This is an efficient way to schedule tasks for later execution and of course if you can leverage other features of Spring effectively then you have ultimate power in your hands.

--

--

Ritesh Shergill

Senior Data and Systems Architect | AI ML and Software Architecture Consultations | Career Guidance | Ex VP at JP Morgan Chase | Startup Mentor | Author