-
Notifications
You must be signed in to change notification settings - Fork 0
Scheduler
Last year I was tasked with extending the CronTrigger of Quartz Scheduler. I did this originally in .NET (hence against Quartz.NET). I thought it'd be a good exercise to recreate the code in Clojure. But there was one main function I couldn't figure out how to write. I ended up asking for help at SNH Clojure Group.
If you care to look at Quartz's CronTrigger source code, you'll notice the CronTrigger is pretty hard to extend. So I actually did a rewrite of CronTrigger. And while at it, I changed the way the schedule is written: from Cron expression to JSON, like this:
Schedule: "Everyday at 11:30am"
- Cron: "0 30 11 ? * *"
- JSON: "{ Hour: 11, Minute: 30 }"
JSON was the thing to do for C#/.NET. Now that I'm doing this in Clojure, I wanted to write my schedules like this:
- "{ :hour 11, :minute 30 }"
Nice, better, yes?
Given a schedule, like "Everyday at 11:30am", and a particular time, a Quartz trigger's main task is to calculate when the next scheduled time is.
- next-time function:
F(schedule, time_t) ==> next_time
This is what I had difficulty writing in Clojure. (I eventually did, but the code was ugly.)
Below is a pseudo-code to get this done in a Java-like language:
GregCalendar nextTime(GregCalendar time_t, Schedule sched) {
GregCalendar t = time_t;
boolean rollOver;
while (true) {
[t, rollOver] = nextSecond(t, sched);
if (rollOver)
continue;
[t, rollOver] = nextMinute(t, sched);
if (rollOver)
continue;
[t, rollOver] = nextHour(t, sched);
if (rollOver)
continue;
[t, rollOver] = nextDay(t, sched);
if (rollOver)
continue;
[t, rollOver] = nextMonth(t, sched);
if (rollOver)
continue;
[t, rollOver] = nextYear(t, sched);
if (rollOver)
continue;
break;
}
return t;
}
Above looks simple enough, but I need to explain what nextSecond(), nextMinute() and the likes do. I'll try to do that with a couple of examples
Given a schedule and time_t:
- Schedule:
{ :minute [0 15 30 45], :second [15 45] }(Can somebody help me write this schedule in plain English?) - time_t: 2014/03/26, 20:27:11
The next-time function would calculate:
- next-time: 2014/03/26, 20:30:15
How do we figure?
- We first take the "second" value of
time_t, which is 11. Then we look at the schedule for seconds, which is[15 45], and ask what the next scheduled time after 11 is. It's 15. - We then take the "minute" value of
time_t, which is 27. Then we look at the schedule for minutes, which is[0 15 30 45]. The next scheduled minute is 30.
The operation is pretty simple. If a schedule specifies hours, days, months, and years, we'd repeat pretty much the same operation on those time units.
There is just one complication, however. It's rollover. On to the next example.
- Schedule:
{ :minute [0 15 30 45], :second [15 45] } - time_t: