Discussion:
Scala + Quartz
Tim Perrett
2010-07-26 23:20:37 UTC
Permalink
Hey all,

Has 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

Cheers, Tim
Derek Chen-Becker
2010-07-26 23:39:17 UTC
Permalink
Post by Tim Perrett
Hey all,
Has 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
Cheers, Tim
I had started working on a wrapper but never really got around to
finishing it because the project that required it was canceled. My
biggest frustration with Quartz is that many of the methods take
arguments of type "Class", instead of concrete instances. I couldn't
figure out any sane/elegant way to make that interop well with Scala
function instances.

I would love to see Quartz get some Scala love ;)

Derek
Naftoli Gugenheim
2010-07-27 02:53:08 UTC
Permalink
That sounds like an interesting problem. Could you elaborate? E.g., an
example quartz function and what you want to be able to write in scala.
Post by Derek Chen-Becker
Post by Tim Perrett
Hey all,
Has 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
Cheers, Tim
I had started working on a wrapper but never really got around to
finishing it because the project that required it was canceled. My
biggest frustration with Quartz is that many of the methods take
arguments of type "Class", instead of concrete instances. I couldn't
figure out any sane/elegant way to make that interop well with Scala
function instances.
I would love to see Quartz get some Scala love ;)
Derek
Derek Chen-Becker
2010-07-27 13:56:40 UTC
Permalink
Post by Naftoli Gugenheim
That sounds like an interesting problem. Could you elaborate? E.g., an
example quartz function and what you want to be able to write in scala.
So, let's start with a Scala function:

def worker { println("Hello") }

There's some setup involved before you can actually do anything, but I'm
not so concerned about things that run once at startup and shutdown. The
basic pattern is:

import org.quartz.{Job,JobDetail,JobExecutionContext,SimpleTrigger}
import org.quartz.impl.StdSchedulerFactory
val scheduler = StdSchedulerFactory..getDefaultScheduler()

scheduler.start()

// do work here

scheduler.shutdown()

The interesting/frustrating part is the actual scheduling of jobs. In my
case, I was trying to write a DSL that would allow you to write things like:

schedule worker byCron "*/5 * * * *"
schedule worker at "5am" every 5 minutes
etc.

But Quartz doesn't schedule objects, it schedules classes. So to run the
"worker" function, I have to do:

class RunWorker extends Job {
def execute (ctxt : JobExecutionContext) {
worker()
}
}

scheduler.schedule(new JobDetail("worker", classOf[RunWorker]),
new SimpleTrigger("fireNow"))

Actually, just writing it out again makes me think that I should be able
to do this indirectly. Let me pull up the old code and see if I can get
this working again.

Derek
Derek Chen-Becker
2010-07-27 15:46:29 UTC
Permalink
Post by Derek Chen-Becker
Actually, just writing it out again makes me think that I should be able
to do this indirectly. Let me pull up the old code and see if I can get
this working again.
OK, after revisiting the code I remembered why I bailed. There seems to
be some issue instantiating a Scala anonymous class from within Java. If
I do it in the REPL it works fine:

scala> val test = () => println("Hello")
test: () => Unit = <function0>

scala> test()
Hello

scala> import
org.quartz.{Job,JobDetail,JobExecutionContext,Scheduler,SimpleTrigger,Trigger}
import org.quartz.{Job, JobDetail, JobExecutionContext, Scheduler,
SimpleTrigger, Trigger}

scala> val j = new Job {
| def execute (ctxt : JobExecutionContext) = {
| test()
| }
| }
j: java.lang.Object with org.quartz.Job = $anon$***@9826ac5

scala> val n = j.getClass.newInstance
n: Any = $anon$***@32c5f9fe

But when Quartz tries to instantiate the class I get an exception:

1111 [DefaultQuartzScheduler_QuartzSchedulerThread] ERROR
org.quartz.core.ErrorLogger - An error occured instantiating job to be
executed. job= 'DEFAULT.incrementer'
org.quartz.SchedulerException: Problem instantiating class
'org.scala_libs.scuartz.Scuartz$JobInfo$$anon$1' [See nested exception:
java.lang.InstantiationException:
org.scala_libs.scuartz.Scuartz$JobInfo$$anon$1]
at org.quartz.simpl.SimpleJobFactory.newJob(SimpleJobFactory.java:57)
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:140)
at
org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:365)
Caused by: java.lang.InstantiationException:
org.scala_libs.scuartz.Scuartz$JobInfo$$anon$1
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at org.quartz.simpl.SimpleJobFactory.newJob(SimpleJobFactory.java:55)
... 2 more
1112 [DefaultQuartzScheduler_QuartzSchedulerThread] INFO
org.quartz.simpl.RAMJobStore - All triggers of Job DEFAULT.incrementer
set to ERROR state.


I haven't had time to dig into this much...

Derek
Naftoli Gugenheim
2010-07-29 00:11:48 UTC
Permalink
So it forces you to let it do the instantiation by taking a Class instance,
and the instantiation behaves differently than when you do it yourself. Very
interesting. Is it doing some kind of byte code manipulation or using some
kind of dynamic proxy?
Post by Derek Chen-Becker
Post by Derek Chen-Becker
Actually, just writing it out again makes me think that I should be able
to do this indirectly. Let me pull up the old code and see if I can get
this working again.
OK, after revisiting the code I remembered why I bailed. There seems to
be some issue instantiating a Scala anonymous class from within Java. If
scala> val test = () => println("Hello")
test: () => Unit = <function0>
scala> test()
Hello
scala> import
org.quartz.{Job,JobDetail,JobExecutionContext,Scheduler,SimpleTrigger,Trigger}
import org.quartz.{Job, JobDetail, JobExecutionContext, Scheduler,
SimpleTrigger, Trigger}
scala> val j = new Job {
| def execute (ctxt : JobExecutionContext) = {
| test()
| }
| }
scala> val n = j.getClass.newInstance
1111 [DefaultQuartzScheduler_QuartzSchedulerThread] ERROR
org.quartz.core.ErrorLogger - An error occured instantiating job to be
executed. job= 'DEFAULT.incrementer'
org.quartz.SchedulerException: Problem instantiating class
org.scala_libs.scuartz.Scuartz$JobInfo$$anon$1]
at
org.quartz.simpl.SimpleJobFactory.newJob(SimpleJobFactory.java:57)
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:140)
at
org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:365)
org.scala_libs.scuartz.Scuartz$JobInfo$$anon$1
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at
org.quartz.simpl.SimpleJobFactory.newJob(SimpleJobFactory.java:55)
... 2 more
1112 [DefaultQuartzScheduler_QuartzSchedulerThread] INFO
org.quartz.simpl.RAMJobStore - All triggers of Job DEFAULT.incrementer
set to ERROR state.
I haven't had time to dig into this much...
Derek
Tim Perrett
2010-07-27 12:57:42 UTC
Permalink
Interesting. What was your use case particularly? I basically need to
have a fairly complex set of "phases" that have actions attached to
them when those phases could happen XX time in the future.

I have no real experience with Quartz, so i'm not aware of the
pitfalls - anything you would point out as a real gotcha?

Cheers, Tim
Post by Derek Chen-Becker
Post by Tim Perrett
Hey all,
Has 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
Cheers, Tim
I had started working on a wrapper but never really got around to
finishing it because the project that required it was canceled. My
biggest frustration with Quartz is that many of the methods take
arguments of type "Class", instead of concrete instances. I couldn't
figure out any sane/elegant way to make that interop well with Scala
function instances.
I would love to see Quartz get some Scala love ;)
Derek
Sam Stainsby
2010-07-27 23:43:20 UTC
Permalink
Post by Tim Perrett
Has 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']
Loading...