Post by Tim PerrettHas anyone done anything with Quartz and Scala?
http://www.quartz-scheduler.org/
Im interested to hear of anyone else's experience or abstractions using
Quartz from scala. Im thinking some kind of actor wrapper over the
scheduling api might be quite nice
I went down that path for a client project but gave up that particular
avenue for scheduling. We use an object database, and so needed to
create a Quartz storage provider. The storage SPI was horrendous, and
worse, seems to be required to calculate when the next job is due to run
- isn't that really the gist of a scheduler in any case? I really wanted
to use Joda Time as well ...
Instead of using Quartz, I have been experimenting with pure Scala (v.
2.8) scheduling and making my first foray into actors. The main classes
for in-memory scheduling are here:
http://uniscala.svn.sourceforge.net/viewvc/uniscala/trunk/uniscala-
schedule/src/main/scala/net/uniscala/schedule/mem/Schedule.scala?
view=markup
http://uniscala.svn.sourceforge.net/viewvc/uniscala/trunk/uniscala-
schedule/src/main/scala/net/uniscala/schedule/mem/Job.scala?view=markup
This is experimental code - I'm just using symbols and tuples for
messages at the moment. Keep in mind this is my first attempt to use
actors - happy to take feedback. A schedule still spawns a thread per
job, so I need to fix that. Our client's jobs run weekly or monthly, so
thread resource concerns haven't been a priority. The license is
currently GPL but that can change - it is just the default we use since
the database requires it. However, this module is independent of the
database.
Schedule instances are designed to serialize/deserialize in a sensible
way, and so can be stored directly into an object database. Thus I
haven't bothered with a storage SPI.
Another useful class to go with Scheudle is this utility that converts
cron patterns into a stream of Joda DateTimes:
http://uniscala.svn.sourceforge.net/viewvc/uniscala/trunk/uniscala-
schedule/src/main/scala/net/uniscala/schedule/cron/CronStream.scala?
view=markup
You can do things like this:
scala> val cron = CronStream("0 0 * * *")
scala> cron.head
res0: org.joda.time.DateTime = 2010-07-29T00:00:00.000+10:00
scala> cron.tail.head
res1: org.joda.time.DateTime = 2010-07-30T00:00:00.000+10:00
(CronStream doesn't yet support 'slash' entries such as '*/15', or day
abbreviations 'S','M','T','W', ... - simple to do but I haven't gotten
around to it)
Putting it together, you can define an 'Action':
class MyAct extends CancellableAction[Any] with Progressable {
override def doAction = // some potentially long operation
override def cancelAction = // handle cancellation
override def progressAsInt = // calculate progress in range 0 .. 100
}
(A basic action really only needs the 'doAction' method)
And schedule it:
val s = new Schedule(() => new MyAct, CronStream("* * * * *"))
(you can use any Traversable[ReadableInstant] instead of a CronStream,
such as a List[DateTime])
You then need to keep the schedule ticking over (there will be a higher
level 'Scheduler' class for this at some stage): e.g.
while(true) { s.update; Thread.sleep(1000) }
And then start the schedule:
s.run
You can also specify whether trigger times in a schedule are:
1. skipped on 'overlap' (a prior job is still running) [the default is
'true']
2. skipped if a trigger time is missed (eg. the application was down at
the time [the default is 'true']