# Scheduling Periodic Tasks

Modern applications often need to run specific tasks periodically. In this guide, you learn how to schedule periodic tasks.

| If you need a clustered scheduler use the Quartz extension.

## Prerequisites

To complete this guide, you need:

- Roughly 15 minutes

- An IDE

- JDK 17+ installed with `JAVA_HOME` configured appropriately

- Apache Maven 3.9.9

- Optionally the Quarkus CLI if you want to use it

- Optionally Mandrel or GraalVM installed and configured appropriately if you want to build a native executable (or Docker if you use a native container build)

## Architecture

In this guide, we create a straightforward application accessible using HTTP to get the current value of a counter. This counter is periodically (every 10 seconds) incremented.

## Solution

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

Clone the Git repository: `git clone https://github.com/quarkusio/quarkus-quickstarts.git`, or download an archive.

The solution is located in the `scheduler-quickstart` directory.

## Creating the Maven project

First, we need a new project. Create a new project with the following command:

CLI

    quarkus create app org.acme:scheduler-quickstart \
        --extension='rest,scheduler' \
        --no-code
    cd scheduler-quickstart

To create a Gradle project, add the `--gradle` or `--gradle-kotlin-dsl` option.

For more information about how to install and use the Quarkus CLI, see the Quarkus CLI guide.

Maven

    mvn io.quarkus.platform:quarkus-maven-plugin:3.26.0.CR1:create \
        -DprojectGroupId=org.acme \
        -DprojectArtifactId=scheduler-quickstart \
        -Dextensions='rest,scheduler' \
        -DnoCode
    cd scheduler-quickstart

To create a Gradle project, add the `-DbuildTool=gradle` or `-DbuildTool=gradle-kotlin-dsl` option.

For Windows users:

- If using cmd, (don’t use backward slash `\` and put everything on the same line)

- If using Powershell, wrap `-D` parameters in double quotes e.g. `"-DprojectArtifactId=scheduler-quickstart"`

It generates a new project including:

- a landing page accessible on `http://localhost:8080`

- example `Dockerfile` files for both `native` and `jvm` modes

- the application configuration file

The project also imports the Quarkus REST (formerly RESTEasy Reactive) and scheduler extensions.

If you already have your Quarkus project configured, you can add the `scheduler` extension to your project by running the following command in your project base directory:

CLI

    quarkus extension add scheduler

Maven

    ./mvnw quarkus:add-extension -Dextensions='scheduler'

Gradle

    ./gradlew addExtension --extensions='scheduler'

This will add the following to your build file:

pom.xml

    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-scheduler</artifactId>
    </dependency>

build.gradle

    implementation("io.quarkus:quarkus-scheduler")

## Creating a scheduled job

In the `org.acme.scheduler` package, create the `CounterBean` class, with the following content:

    package org.acme.scheduler;

    import java.util.concurrent.atomic.AtomicInteger;
    import jakarta.enterprise.context.ApplicationScoped;
    import io.quarkus.scheduler.Scheduled;
    import io.quarkus.scheduler.ScheduledExecution;

    @ApplicationScoped              (1)
    public class CounterBean {

        private AtomicInteger counter = new AtomicInteger();

        public int get() {  (2)
            return counter.get();
        }

        @Scheduled(every="10s")     (3)
        void increment() {
            counter.incrementAndGet(); (4)
        }

        @Scheduled(cron="0 15 10 * * ?") (5)
        void cronJob(ScheduledExecution execution) {
            counter.incrementAndGet();
            System.out.println(execution.getScheduledFireTime());
        }

        @Scheduled(cron = "{cron.expr}") (6)
        void cronJobWithExpressionInConfig() {
           counter.incrementAndGet();
           System.out.println("Cron expression configured in application.properties");
        }
    }

1 | Declare the bean in the application scope
2 | The `get()` method allows retrieving the current value.
3 | Use the `@Scheduled` annotation to instruct Quarkus to run this method every 10 seconds provided a worker thread is available (Quarkus is using 10 worker threads for the scheduler). If it is not available the method invocation should be re-scheduled by default i.e. it should be invoked as soon as possible. The invocation of the scheduled method does not depend on the status or result of the previous invocation.
4 | The code is pretty straightforward. Every 10 seconds, the counter is incremented.
5 | Define a job with a cron-like expression. The annotated method is executed at 10:15am every day.
6 | Define a job with a cron-like expression `cron.expr` which is configurable in `application.properties`.

## Updating the application configuration file

Edit the `application.properties` file and add the `cron.expr` configuration:

    # By default, the syntax used for cron expressions is based on Quartz - https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html
    # You can change the syntax using the following property:
    # quarkus.scheduler.cron-type=unix
    cron.expr=*/5 * * * * ?

## Creating the REST resource

Create the `CountResource` class as follows:

    package org.acme.scheduler;

    import jakarta.inject.Inject;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.Produces;
    import jakarta.ws.rs.core.MediaType;

    @Path("/count")
    public class CountResource {

        @Inject
        CounterBean counter;            (1)

        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String hello() {
            return "count: " + counter.get();  (2)
        }
    }

1 | Inject the `CounterBean`
2 | Send back the current counter value

## Package and run the application

Run the application with:

CLI

    quarkus dev

Maven

    ./mvnw quarkus:dev

Gradle

    ./gradlew --console=plain quarkusDev

In another terminal, run `curl localhost:8080/count` to check the counter value. After a few seconds, re-run `curl localhost:8080/count` to verify the counter has been incremented.

Observe the console to verify that the message `Cron expression configured in application.properties` has been displayed indicating that the cron job using an expression configured in `application.properties` has been triggered.

As usual, the application can be packaged using:

CLI

    quarkus build

Maven

    ./mvnw install

Gradle

    ./gradlew build

And executed with `java -jar target/quarkus-app/quarkus-run.jar`.

You can also generate the native executable with:

CLI

    quarkus build --native

Maven

    ./mvnw install -Dnative

Gradle

    ./gradlew build -Dquarkus.native.enabled=true

## Scheduler Configuration Reference

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property | Type | Default
--- | --- | ---
`quarkus.scheduler.cron-type` The syntax used in CRON expressions. Environment variable: `QUARKUS_SCHEDULER_CRON_TYPE` | `cron4j`, `quartz`, `unix`, `spring`, `spring53` | `quartz`
`quarkus.scheduler.metrics.enabled` Scheduled task metrics will be enabled if a metrics extension is present and this value is true. Environment variable: `QUARKUS_SCHEDULER_METRICS_ENABLED` | boolean | `false`
`quarkus.scheduler.tracing.enabled` Controls whether tracing is enabled. If set to true and the OpenTelemetry extension is present, tracing will be enabled, creating automatic Spans for each scheduled task. Environment variable: `QUARKUS_SCHEDULER_TRACING_ENABLED` | boolean | `false`
`quarkus.scheduler.use-composite-scheduler` By default, only one `Scheduler` implementation is used. If set to `true` then a composite `Scheduler` that delegates to all running implementations is used. Scheduler implementations will be started depending on the value of `quarkus.scheduler.start-mode`, i.e. the scheduler is not started unless a relevant `io.quarkus.scheduler.Scheduled` business method is found. Environment variable: `QUARKUS_SCHEDULER_USE_COMPOSITE_SCHEDULER` | boolean | `false`
`quarkus.scheduler.enabled` If schedulers are enabled. Environment variable: `QUARKUS_SCHEDULER_ENABLED` | boolean | `true`
`quarkus.scheduler.overdue-grace-period` Scheduled task will be flagged as overdue if next execution time is exceeded by this period. Environment variable: `QUARKUS_SCHEDULER_OVERDUE_GRACE_PERIOD` | Duration | `1S`
`quarkus.scheduler.start-mode` Scheduler can be started in different modes. By default, the scheduler is not started unless a `io.quarkus.scheduler.Scheduled` business method is found. Environment variable: `QUARKUS_SCHEDULER_START_MODE` | `normal`, `forced`, `halted` | `normal`

| About the Duration format To write duration values, use the standard `java.time.Duration` format. See the Duration#parse() Java API documentation for more information. You can also use a simplified format, starting with a number: If the value is only a number, it represents time in seconds. If the value is a number followed by `ms`, it represents time in milliseconds. In other cases, the simplified format is translated to the `java.time.Duration` format for parsing: If the value is a number followed by `h`, `m`, or `s`, it is prefixed with `PT`. If the value is a number followed by `d`, it is prefixed with `P`.