We’re using quartz (the java API) for job scheduling. This works well. Now I’m trying to generalize some things with it. The quartz api requires a job class as parameter which extends the Job interface. This makes passing in arguments via a constructor impossible.
We have a set of jobs which should all do the same, execute a check, if true then invoke an action, for example:
class SimpleJob extends Job {
def execute(context: JobExecutionContext) {
val check = classOf[SimpleCheck].asInstanceOf[Class[Check]].newInstance()
val result = check.execute(context.getJobDetail.getJobDataMap)
if (result.shouldInvokeAction) {
Action(result).execute
}
}
A quartz job is then instantiated by invoking:
newJob(classOf[SimpleJob]).with...
This works.
The goals is to re-use this logic for different types of checks
Question: Can I use the scala type system in such a way that I can have one typed JobClass which can be re-used to execute any subclass of Check?
I’ve come up with the following solution:
class GenericJobRule[J <: Check](implicit m: Manifest[J]) extends Job {
def execute(context: JobExecutionContext) {
val check = m.erasure.newInstance().asInstanceOf[J]
val result = check.execute(context.getJobDetail.getJobDataMap)
if (result.shouldInvokeAction) {
Action(result).execute
}
}
}
A job can now be instantiated like this:
newJob(classOf[GenericJobRule[PerformanceCheck]])
This works, however I think the instantiation and casting kind-of bypasses the whole idea of type-checking. Is there any better way to do this? Maybe we should rethink our design as well…
Thanks, Albert
Maybe one possibilty is to use type classes. I tried to implement it (simplified a few things), the downside is
that the specific job rules always must implement the doCheck method (for more on type classes, see here and here):
[EDIT]: replaced abstract class by trait. No constructor argument is needed anymore.
[EDIT2]: Forget about type classes (in this case). Made it much simpler now. Just a trait and an implementing class per check. What do you think of that?
A job can now be instantiated like this:
[EDIT3]: Here is another possibility if GenericJobRule is an abstract class:
Then you can create the specific job rules on the fly: