Add RFC 5545 RRULE trigger support (dependency-free implementation) - #1499
Add RFC 5545 RRULE trigger support (dependency-free implementation)#1499sideeffffect wants to merge 3 commits into
Conversation
Introduces an RRULE analogue of the existing CronTrigger stack, allowing schedules to be defined with RFC 5545 recurrence rules (iCalendar RRULE): * RRuleExpression - parser/evaluator for RRULE (analogue of CronExpression) * RRuleTrigger (+Impl) - the trigger interface and implementation * RRuleScheduleBuilder - DSL builder (rruleSchedule(...), daily/weekly/monthly) * RRuleCalendar - excludes times matching an RRULE * RRuleTriggerPersistenceDelegate + QRTZ_RRULE_TRIGGERS DDL for every vendor * RRuleTriggerSupport - JMX open-type support and wiring The recurrence engine is implemented from scratch (no third-party library), following the expand/limit semantics of RFC 5545 section 3.3.10. It supports FREQ, INTERVAL, COUNT, UNTIL, WKST, BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY (including ordinals), BYHOUR, BYMINUTE, BYSECOND and BYSETPOS. The trigger's start-time plays the role of the RFC 5545 DTSTART anchor, exactly as a CronTrigger uses its start-time as a lower bound. Includes unit tests (expression, schedule builder, trigger, calendar) and a Derby JDBC integration test exercising the persistence delegate end to end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Ondra Pelech <ondra.pelech@gmail.com>
2bc44b5 to
43d3058
Compare
|
+1 for the dependency free implementation! |
|
I agree with @melloware that the dependency-free version is better overall. I'll try to have a more thorough review this morning. |
|
@sideeffffect , I appreciate this new feature, and know that many users will. Could I trouble you+Claude to include some docs / examples for the new trigger type? e.g.: https://github.com/quartz-scheduler/quartz/tree/main/examples |
Adds an examples module entry (analogous to example3 for cron) that schedules several jobs using RRuleScheduleBuilder.rruleSchedule(...), covering SECONDLY / WEEKLY / MONTHLY frequencies, ordinal BYDAY (last Friday), BYMONTHDAY lists, BYDAY limiting and a COUNT-bounded rule. Wires the runExample16 Gradle task and lists it in examples_guide.txt. Requested in review on PR quartz-scheduler#1499. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Ondra Pelech <ondra.pelech@gmail.com>
|
Thanks for the quick look, @jhouserizer and @melloware! 🙏 I've added in-repo docs/examples in
Run it with The tutorials page you linked lives in the separate |
|
Docs are up as well: I opened a companion tutorial PR against the site repo — quartz-scheduler/quartz-scheduler.org-site#123 (a "RRuleTrigger Tutorial" page mirroring the CronTrigger one, linked from the 2.5.x tutorials index). |
|
Hello @jhouserizer , thanks for looking at this. Please let me know if there's anything more I can do 🙏 |
The Liquibase changelog (liquibase.quartz.init.xml) creates the Quartz schema for Liquibase-based setups. Add the QRTZ_RRULE_TRIGGERS table (with its primary key and foreign key to QRTZ_TRIGGERS), mirroring QRTZ_CRON_TRIGGERS, so the RRULE trigger type is available there too, matching the per-vendor SQL scripts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Ondra Pelech <ondra.pelech@gmail.com>
|
ping 😸 @melloware @jhouserizer |
Summary
Adds first-class support for RFC 5545 RRULE (iCalendar recurrence rules) as a scheduling option, mirroring the existing
CronTriggerstack. New public API:RRuleExpressionCronExpressionRRuleTrigger/RRuleTriggerImplCronTrigger/CronTriggerImplRRuleScheduleBuilderCronScheduleBuilderRRuleCalendarCronCalendarRRuleTriggerPersistenceDelegateCronTriggerPersistenceDelegateRRuleTriggerSupport(JMX)CronTriggerSupportDesign notes
expand/limitsemantics of the table in RFC 5545 §3.3.10. It was cross-validated againstorg.dmfs:lib-recuracross a broad battery of rules and time zones (including DST transitions) to confirm equivalence on the mainstream rule set.DTSTARTanchor: RFC 5545 defines anRRULErelative to aDTSTART. In Quartz that anchor is supplied by the trigger's mandatorystartTime, exactly as aCronTriggeruses its start-time as a lower bound. Following the RFC, the start instant only fires if it actually matches the pattern.QRTZ_RRULE_TRIGGERStable (RRULE string + time-zone) with DDL added to every vendor script underjdbcjobstore, a registeredRRuleTriggerPersistenceDelegate, and a newTTYPE_RRULEdiscriminator. The delegate loads via its own dedicatedSELECT(rather than the inlined bulk join) because the sharedTIME_ZONE_IDcolumn would collide inSELECT_BULK_TRIGGERS_BASE.FREQ,INTERVAL,COUNT,UNTIL,WKST,BYMONTH,BYWEEKNO,BYYEARDAY,BYMONTHDAY,BYDAY(incl. ordinals),BYHOUR,BYMINUTE,BYSECOND,BYSETPOS.HOURLYrule fires once across a spring-forward transition (matching Quartz's cron behaviour) rather than emitting a duplicated wall-clock hour.Tests & docs
example16(RRuleTriggerExample) demonstrating the DSL, runnable via../gradlew :examples:runExample16.Note
This is the dependency-free implementation. An alternative PR built on
org.dmfs:lib-recur(#1498) exposed the identical public API and test suite; it has been closed in favour of this one per the discussion below.🤖 Generated with Claude Code