Scala: Create a generic Quartz Job class

quartz-scheduler, scala

Solution

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?

trait GenericJobRule[J <: Check] extends Job {

  val genericCheck: J

  def execute(context: JobExecutionContext) {
    val result = genericCheck.execute(context.getJobDataMap)

    if (result.shouldInvokeAction) {
      Action(result).execute
    }
  }  
}

class JobRule1 extends GenericJobRule[SimpleCheck] {
  override val genericCheck = new SimpleCheck
}

A job can now be instantiated like this:

newJob(classOf[JobRule1])

[EDIT3]: Here is another possibility if GenericJobRule is an abstract class:

abstract class GenericJobRule[J <: Check] extends Job {

  val genericCheck: J

  def execute(context: JobExecutionContext) {
    val result = genericCheck.execute(context.getJobDataMap)

    if (result.shouldInvokeAction) {
      Action(result).execute
    }
  }  
}

Then you can create the specific job rules on the fly:

val myJobRule = new GenericJobRule[SimpleCheck] { override val genericCheck = new SimpleCheck }

Problem

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

Original source

Related problems