1. Quartz

Task scheduler module using Quartz.

1.1. Usage

1) Add the dependency:

Maven
Gradle
<dependency>
  <groupId>io.jooby</groupId>
  <artifactId>jooby-quartz</artifactId>
  <version>3.5.5</version>
</dependency>

2) Install Quartz. Add SampleJob:

Java
Kotlin
import io.jooby.quartz.QuartzModule;

{
  install(new QuartzModule(SampleJob.class));
}

3) Creates SampleJob:

Java
Kotlin
import io.quartz.Scheduled;

import org.quartz.Job;

public class SampleJob implements Job {            (1)

  @Scheduled("1m")                                 (2)
  public void execute(JobExecutionContext ctx) {
    ...
  }
}
1 Implements org.quartz.Job
2 Creates a trigger using the Scheduled annotation

1.2. Jobs

A Job must implements org.quartz.Job interface or org.quartz.InterruptableJob as described in Quartz documentation or if you prefer just annotates an arbitrary method with the Scheduled annotation:

Without Job interface
Java
Kotlin
import io.quartz.Scheduled;

public class SampleJob {

  @Scheduled("1m")
  public void everyMinute() {
    ...
  }
}

Jooby generates a job key using the class and method names:

  • SampleJob.everyMinute ⇒ group: SampleJob, name: everyMinute

This approach allows you to define multiple job methods:

Job Group
Java
Kotlin
import io.quartz.Scheduled;

public class SampleJob {

  @Scheduled("1m")
  public void everyMinute() {
    ...
  }

  @Scheduled("1h")
  public void everyHour() {
    ...
  }
}

A job method must follow these rules:

  • Must be a public method

  • Possible arguments: none (zero), JobExecutionContext or AtomicBoolean. The atomic boolean is used to notify job method about interruption requests.

1.2.1. Job Factory

Job classes are required to have a default constructor (public and without arguments). For more complex uses cases when the job is required to interact with other application services you have two options:

  • Creates your own JobFactory

  • Uses a dependency injection module, like Guice

Here is an example and reflection-free JobFactory implementation:

Custom Job Factory
{

  Scheduler scheduler = QuartzModule.newScheduler(this);

  scheduler.setJobFactory((bundle, sch) -> {
    Class jobClass = bundle.getJobDetail().getJobClass();
    if (jobClass == MyJob.class) {
      return new MyJob(...);
    }
  });

  install(new QuartzModule(MyJob.class));
}

This other example uses Guice as dependency provider:

Guice provisioning
import jakarta.inject.Inject;

{
  install(new GuiceModule());

  install(new QuartzModule(MyJob.class));
}

public class MyJob {

  @Inject
  public MyJob(SomeService someService) {
    this.someService = someService;
  }
}

1.3. Triggers

The Scheduled annotation supports simple and cron triggers as well as property references:

Run every hour, repeat for ever:
@Scheduled("1h")
Run every hour, repeat 3 times:
@Scheduled("1h; repeat=3")
Run every hour with a start delay of 5m:
@Scheduled("1h; delay=5m")
Cron, every 5 minutes
@Scheduled("0 0/5 * * * ?")
Cron, fires every 5 minutes, at 10 seconds after the minute (i.e. 10:00:10 am, 10:05:10 am, etc.)
@Scheduled("10 0/5 * * * ?")
Property reference
@Scheduled("myjob.trigger")

The myjob.trigger must be defined in your application property file. It could be a cron or simple expression.

1.4. Jdbc JobStore

Quartz module uses a RAMStore by default. To store job into database you need follow these steps:

1) Add the dependency:

Maven
Gradle
<dependency>
  <groupId>io.jooby</groupId>
  <artifactId>jooby-hikari</artifactId>
  <version>3.5.5</version>
</dependency>

2) Install Hikari and Quartz

Java
Kotlin
import io.jooby.hikari.HikariModule;
import io.jooby.quartz.QuartzModule;

{
  install(new HikariModule());

  install(new QuartzModule(SampleJob.class));
}

3) Set database properties and jdbc store

application.conf
db.url = "jdbc:mysql://localhost/mydb"
db.user = "myuser"
db.password = "mypassword"

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

1.5. Configuration

Configuration from properties files is fully supported, just need to add quartz properties to your application configuration file:

Thread Pool
# Set number of threads to use, default is to use the number of available processor
org.quartz.threadPool.threadCount = 2

Checkout the Quartz configuration section to see a list of all available configuration properties.

Programmatic configuration is supported by providing your own Scheduler:

Custom Scheduler
{

  Scheduler scheduler = QuartzModule.newScheduler(this);
  // configure scheduler as you need it

  install(new QuartzModule(scheduler, SampleJob.class));

}

1.5.1. Disable Jobs at Startup

Another nice feature of Quartz module is the ability to turn on/off jobs at start-up time. The turn on/off job is implementing by pausing (job off) and then resume (job ob) operations of scheduler.

Pausing Job at startup time
org.quartz.jobs.SampleJob.execute.enabled = false

Now the job SampleJob.execute will be paused at startup time.

1.6. REST API

This modules comes with a simple REST API (sort of) to manage job and triggers:

Quartz API
Java
Kotlin
import io.jooby.quartz.QuartzApp
import io.jooby.quartz.QuartzModule;

{
  install(new QuartzModule(SampleJob.class));

  use("/scheduler", new QuartzApp());
}

The API supports all these operations:

List all job keys
GET /
List job information
GET /{group}/{name}
Trigger/force a job execution.
GET /{group}/{name}/trigger

Query parameters are added as JobDataMap parameters

Attempt to interrupt an existing job execution
GET /{group}/{name}/interrupt

As described in Quartz documentation it is a Job responsibility to decide when and how to abort an existing execution. So this operation all it is does is to call InterruptableJob.interrupt method to notify about interrupt requests.

Pause execution of Job
GET /{group}/{name}/pause

This operation doesn’t interrupt an existing running job, just pause future executions.

Resumes a previously paused Job
GET /{group}/{name}/resume
Deletes a job
DELETE /{group}/{name}