Scheduling Tasks With Spring Boot and Kotlin

In our fourth article, we will learn how to schedule tasks in Spring Boot Kotlin application using Scheduled annotation and TaskScheduler.
A featured image for category: Spring

1. Introduction

The Spring Framework provides great support for asynchronous execution and scheduling of tasks.

In this article, we will learn how to schedule tasks in Spring Boot Kotlin application. We will see how it is done with @Scheduled annotation, as well as how to create tasks in the runtime.

Video Tutorial

If you prefer video content, then check out my video:

If you find this content useful, please leave a subscription  ๐Ÿ˜‰

2. Enable Scheduling

But before we will start creating our tasks, we need to enable scheduling for our application. Therefore, let’s create a Config class which we will annotate with @EnableScheduling annotation:

@Configuration
@EnableScheduling
class Config {}

The @EnableScheduling annotation ensures that a background task executor is created. Without it, nothing gets scheduled.

Make a real progress thanks to practical examples, exercises, and quizzes.

Image presents a Kotlin Course box mockup for "Kotlin Handbook. Learn Through Practice"

3. Create Scheduled Tasks Using @Scheduled

3.1. Fixed RateTask

Let’s start with implementing our first task. To do that, we will use fixedRate property along with a number of milliseconds that we would like to elapse between each function invocation:

@Scheduled(fixedRate = 3000)
fun fixedRateScheduledTask() {
    //Some code here
}

3.2. Fixed Delay Task

If we would like the time to be measured from the completion of each preceding invocation, then we should use fixedDelay property instead:

@Scheduled(fixedDelay = 3000)
fun fixedDelayScheduledTask() {
    //Some code here
}

3.3. Initial delay task

Additionally, we can specify the number of milliseconds to delay before the first execution of a task:

@Scheduled(initialDelay = 2000, fixedDelay = 3000)
fun initialDelayScheduledTask() {
    //Some code here
}

3.4. Cron task

Sometimes, we would like to schedule a task that needs to be invoked in a more specific interval. In such a case, we can provide a cron expression:

@Scheduled(cron = "0 0 6,15 * * MON-FRI")
fun cronScheduledTask() {
    //Some code here
}

The above task will be invoked at 6:00 AM and 7:00 PM weekdays. To see more examples of cron expressions, please visit the documentation.

3.5. Cron task using value injection

All of the above examples have a common flaw- we can’t change trigger metadata without the modification of source code. To change that, let’s implement our cron task using Spring Expression Language:

@Scheduled(cron = "\${my.cron.value}")
fun cronScheduledTaskUsingProperties() {
    //Some code here
}

Secondly, let’s add the following code to the application.yml file:

my:
  cron:
    value: ${MY_CRON_VALUE}

As we can see above, we can pass the desired cron expression as the environment variable making our application more flexible.

Please notice, that Spring v3.2.2 has added 3 properties to the @Scheduled annotation: fixedDelayString, fixedRateString, and initialDelayString, which allow us to pass the parameter the same way.

4. Schedule Tasks in the Runtime

As we can see in the above examples, defining tasks with @Scheduled is really straightforward. But what if we would like to have deeper control over our tasks in the runtime?

To achieve that, we will use the TaskScheduler interface, which abstracts the scheduling of Runnables based on different kinds of triggers.

4.1. Create the Service

Let’s start with implementing ManualTaskScheduleService class, which will take the instance of TaskScheduler as the constructor’s parameter:

@Service
class ManualTaskScheduleService(
        private val scheduler: TaskScheduler
) {}

As the next step, we will add two properties to our class:

  • taskId– which will be used to generate identifiers for our tasks,
  • futures– mutable map, which will store our ScheduledFutures representing pending completion of the tasks:
val futures: MutableMap<Int, ScheduledFuture<*>> = HashMap()
val taskId = AtomicInteger()

Then, we can start implementing functions responsible for task management. Let’s create addNewTask function, which will take the Runnable to execute as a parameter:

fun addNewTask(task: Runnable): Int {
    val taskPeriod = Duration.ofSeconds(2)
    val scheduledTaskFuture = scheduler.scheduleAtFixedRate(task, taskPeriod)

    val id = taskId.incrementAndGet()
    futures[id] = scheduledTaskFuture

    return id
}

This function will schedule the given Runnable, starting ASAP and invoking it every two seconds. In addition, it will also add the created task’s future to the map so that we will be able to refer to it later.

Finally, we can implement the removeTaskFromScheduler function:

fun removeTaskFromScheduler(id: Int) {
    futures[id]?.let {
        it.cancel(true)
        futures.remove(id)
    }
}

As we can see, removeTaskFromScheduler takes the id of the task as the parameter. Moreover, it looks for the desired future in the futures map, and if it is found, then cancel and removes it from the map.

5. Create Example Controller

As one of the last steps, we will create the example REST controller. Let’s implement the TestController class as follows:

@RestController
class TestController(
        private val manualTaskScheduleService: ManualTaskScheduleService
) {
    private val logger: Logger = LoggerFactory.getLogger(TestController::class.java)
}

Then, let’s add createTask function which will be responding to POST requests to /task endpoint:

@PostMapping("/task")
fun createTask(@RequestParam name: String): ResponseEntity<Int> {
    val task = Runnable { logger.info("Passed name: $name") }

    val createdTaskId = manualTaskScheduleService.addNewTask(task)

    return ResponseEntity.ok(createdTaskId)
}

Finally, let’s create deleteTask function, which will be used to delete created tasks:

@DeleteMapping("/task/{taskId}")
fun deleteTask(@PathVariable taskId: Int) {
    manualTaskScheduleService.removeTaskFromScheduler(taskId)
}

6. Verification

To verify whether manual tasks scheduling is working as expected, let’s query our endpoint to create two new tasks:

curl -X POST localhost:8080/task?name=Piotr
curl -X POST localhost:8080/task?name=John

After that, we should get identifiers of our tasks and we should see the results being printed in the logs of the application as well.

So finally, let’s delete our tasks:

curl -X DELETE localhost:8080/task/1
curl -X DELETE localhost:8080/task/2

And that’s it. Both of our tasks have been canceled.

7. Conclusion

In this article, we’ve learned how to schedule tasks in Spring Boot Kotlin application. We’ve covered how to use @Scheduled annotation to create tasks with different interval. Moreover, we explored how to manage tasks in the runtime.

For the source code, please visit our project on GitHub.

If you are reading this paragraph, I want to know that I am more than happy that you decided to spend your time learning with Codersee and I hope that this article helped you to learn something new. If you have any ideas or comments that you would like to share with us, please let us know in the comments below, by our Facebook page or group, or by using our contact form.

Share this:

Related content

Newsletter
Image presents 3 ebooks with Java, Spring and Kotlin interview questions.

Never miss any important updates from the Kotlin world and get 3 ebooks!

You may opt out any time. Terms of Use and Privacy Policy