Portable, CDI-based job scheduler for Jakarta EE 10/11.
Inject one service, submit a method call, and let Ratchet persist, claim, execute, retry, and observe the work — no heavyweight framework, no proprietary runtime.
@ApplicationScoped
public class OrderService {
@Inject JobSchedulerService scheduler;
public void placeOrder(Order order) {
// Persisted, claimed, executed on a worker, retried on failure.
scheduler.enqueueNow(() -> processOrder(order.getId()));
}
void processOrder(UUID orderId) { /* your business logic */ }
}Full documentation → ratchet.run
- Zero-ceremony CDI.
@Inject JobSchedulerServiceand go. No XML, no scheduler boilerplate, no separate daemon. - Real workflows, not just timers. Chaining, conditional branching on results, batches, and jobs that park on an external signal until an approval or webhook resumes them.
- Resilience built in. Retries with backoff, a circuit breaker, and a dead-letter queue — no Resilience4j, no Flyway, no extra runtime deps.
- Stores you can prove. MySQL, PostgreSQL, and MongoDB ship out of the box, each verified by a reusable store TCK. Bring your own and run the same conformance suite.
- Customizable to the core — see below.
- Portable. Plain Jakarta EE 10/11. Runs on WildFly, Open Liberty, Payara, GlassFish, and friends.
Almost every moving part of Ratchet is an SPI you can swap with a single CDI @Alternative bean — no forking, no config files. Override one interface and the engine picks it up at deploy time:
@Alternative
@Priority(jakarta.interceptor.Interceptor.Priority.APPLICATION)
@ApplicationScoped
public class PaymentRetryPolicy implements RetryPolicy {
@Override
public boolean shouldRetry(int attempt, Throwable cause) {
// Stop early on a hard decline; otherwise let maxRetries decide.
return !(cause instanceof PaymentDeclinedException);
}
}Swap the retry logic, circuit-breaker behavior, polling cadence, thread/executor strategy, payload encryption, key provider, metrics sink, per-job logging, node identity, cluster wakeups, the deserialization allowlist, and more — over two dozen extension points in all, each with a sensible default you only replace when you need to.
SPI extension guide → · Full SPI reference →
<dependencyManagement>
<dependencies>
<dependency>
<groupId>run.ratchet</groupId>
<artifactId>ratchet-bom</artifactId>
<version>0.1.1-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>run.ratchet</groupId>
<artifactId>ratchet-api</artifactId>
</dependency>
<dependency>
<groupId>run.ratchet</groupId>
<artifactId>ratchet</artifactId>
</dependency>
<!-- Pick your store: ratchet-store-{postgresql,mysql,mongodb} -->
<dependency>
<groupId>run.ratchet</groupId>
<artifactId>ratchet-store-postgresql</artifactId>
</dependency>
</dependencies>Then three things before your first job runs:
- Provide a
ClassPolicy. Ratchet refuses to start without a deserialization allowlist for job payloads — a security default, not a hurdle. A one-line@Alternativebean naming your packages does it. - Apply the schema. SQL stores ship plain DDL (
ddl/*-schema.sql) — apply it however you manage migrations. MongoDB initializes itself. - Schedule a job.
scheduler.enqueueNow(() -> work()), or use the builder for delays, priority, and options.
The Quick Start guide walks through all three with copy-paste snippets.
// Multi-step workflow with conditional branching
scheduler.enqueue(() -> riskService.assess(applicationId))
.whenResult(RiskConditions::isLowRisk, () -> autoApprove(applicationId))
.whenResult(RiskConditions::isHighRisk, () -> manualReview(applicationId))
.thenOnFailure(() -> escalate(applicationId))
.submit();
// Declarative cron scheduling
@Recurring(cron = "0 0 2 * * ?", name = "Nightly Cleanup")
public void performCleanup() { /* runs at 2 AM UTC */ }
// Park a job until an external signal arrives (or it times out)
scheduler.enqueue(() -> shipOrder(orderId))
.awaitSignal("order:" + orderId + ":approved", Duration.ofHours(24))
.submit();More — batches, callbacks, encrypted payloads, event observation, job control: Workflows · Recurring jobs · Signals · Batches · Circuit breakers · Payload encryption · API reference
ratchet-showcase is a runnable Jakarta EE WAR with an order-fulfillment dashboard demonstrating live workflow chains, conditional fraud-review signals, retries, resource limits, queue health, and Prometheus metrics. See testing/ratchet-showcase/README.md to run it on WildFly, Payara, Open Liberty, or GlassFish against PostgreSQL, MySQL, or MongoDB.
flowchart TD
app["Your Application"]
api["ratchet-api<br/>JobSchedulerService, JobBuilder, events, annotations, SPI"]
ri["ratchet<br/>Poller, JobTask, CircuitBreaker, RetryEngine, RecurringScheduler, CDI wiring"]
store["ratchet-store-core<br/>Entities + composed JobStore SPI"]
mysql["ratchet-store-mysql"]
postgres["ratchet-store-postgresql"]
mongo["ratchet-store-mongodb"]
app --> api --> ri --> store
store --> mysql & postgres & mongo
Pluggable stores, optional cluster coordinators (PostgreSQL LISTEN/NOTIFY, JMS, Infinispan, Hazelcast), and reference encryption/metrics modules round out the reactor. Module overview & deployment topology →
| Start here | Go deeper |
|---|---|
| Introduction | Core concepts |
| Quick Start | SPI & customization |
| Installation | Deployment & clustering |
| API reference | Troubleshooting |
- Java 17+
- Jakarta EE 10/11 (CDI 4.0/4.1, JPA 3.1/3.2, Interceptors 2.1/2.2, Concurrency 3.0/3.1)
- Runtime Jakarta EE 10/11 server with managed executors (WildFly, Open Liberty, Payara, GlassFish 8, …); plain CDI/test deployments can opt into
StandaloneExecutorProvider - Database MySQL 8+, PostgreSQL 14+, or MongoDB 6+
mvn clean compile # compile all modules
mvn clean test # unit tests
mvn clean verify # unit + integration tests (requires Docker for Testcontainers)
mvn spotless:apply # auto-format (Google Java Format)Ratchet is in 0.1.1-SNAPSHOT. The API is stabilizing; interfaces marked @Incubating may change between alpha releases. Feedback and contributions are welcome.
Ratchet is licensed under the Apache License 2.0.