extensions/quartz/deployment/src/main/resources/META-INF/quarkus-skill.md
Use quartz instead of scheduler when you need: JDBC-backed persistent jobs, clustering, the direct Quartz API for programmatic job management, or misfire handling. For simple in-memory scheduling, quarkus-scheduler alone is sufficient.
@ApplicationScoped
public class Jobs {
@Scheduled(every = "10s", identity = "my-job")
void everyTenSeconds() { /* runs every 10 seconds */ }
@Scheduled(cron = "0 0 8 * * ?", identity = "morning-report")
void dailyAt8am() { /* Quartz cron format (6 fields, includes seconds) */ }
}
void, Uni<Void>, or CompletionStage<Void>, and take no arguments (or ScheduledExecution).identity is used for pause/resume operations and must be unique.quarkus.scheduler.cron-type — see the scheduler reference.io.quarkus.scheduler.Scheduler — Quarkus abstraction for controlling scheduled jobs:
@Inject Scheduler scheduler;
scheduler.pause("my-job");
scheduler.resume("my-job");
boolean running = scheduler.isRunning();
io.quarkus.quartz.QuartzScheduler — Quarkus-native way to access Quartz features, including programmatic job creation and direct access to the underlying Quartz Scheduler:
@Inject QuartzScheduler quartzScheduler;
// Programmatic job via Quarkus API
quartzScheduler.newJob("dynamic-job")
.setInterval("10s")
.setTask(execution -> { /* ... */ })
.schedule();
// Or access the underlying org.quartz.Scheduler directly
org.quartz.Scheduler quartz = quartzScheduler.getScheduler();
Use the Quarkus Scheduler for pause/resume and status. Use QuartzScheduler for programmatic job management and Quartz-specific features.
Implement org.quartz.Job:
public class MyJob implements Job {
@Inject SomeService service; // CDI injection works
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
String param = context.getJobDetail().getJobDataMap().getString("param");
service.process(param);
}
}
Job classes automatically.@DisallowConcurrentExecution to prevent overlapping executions.context.getJobDetail().getJobDataMap().@Scheduled methods CAN run concurrently with themselves.@Nonconcurrent on a @Scheduled method to prevent overlapping executions. Note: unlike SKIP, the SkippedExecution event is never fired when Quartz skips execution.@Scheduled(concurrentExecution = SKIP) — this fires a SkippedExecution event when an execution is skipped.Job classes, use @DisallowConcurrentExecution.quarkus.quartz.store-type=jdbc-cmt
quarkus.quartz.clustered=true
Requires a datasource. Quartz tables must be created manually via SQL migration (Flyway or Liquibase) — see the Quarkus Quartz guide for the required table definitions. Jobs survive application restarts.
@Scheduled methods run during tests by default.quarkus.scheduler.enabled=false.Awaitility:
await().atMost(Duration.ofSeconds(5)).until(() -> counter.get() > 0);
Misfires occur when a job's trigger time passes without the job executing (e.g., the application was down). Configure misfire policies in application.properties:
# Per-job misfire policy (keyed by the job's identity)
quarkus.quartz.misfire-policy."daily-report"=fire-now
# Global defaults for all triggers of a given type
quarkus.quartz.cron-trigger.misfire-policy=smart-policy
quarkus.quartz.simple-trigger.misfire-policy=smart-policy
Available policies (common):
smart-policy (default) — Quartz picks a sensible policy based on the trigger type.fire-now — execute immediately on recovery.ignore-misfire-policy — fire all missed triggers.cron-trigger-do-nothing — skip misfired cron triggers and wait for the next scheduled time.Additional simple-trigger-specific policies: simple-trigger-reschedule-now-with-existing-repeat-count, simple-trigger-reschedule-now-with-remaining-repeat-count, simple-trigger-reschedule-next-with-existing-count, simple-trigger-reschedule-next-with-remaining-count.
"0 */5 * * * ?" not "*/5 * * * *".@Scheduled methods must be on CDI beans — a class that has no scope and declares at least one non-static method annotated with @Scheduled is automatically annotated with @Singleton.@Scheduled on private methods.Scheduler and Quartz Scheduler are different types — inject the right one for your use case.@Scheduled methods), set quarkus.scheduler.start-mode=forced — otherwise the scheduler won't start.