5

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

Albert
  • 2,125
  • 1
  • 19
  • 24

1 Answers1

3

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 }
Christian
  • 4,543
  • 1
  • 22
  • 31
  • I see where you're going. Looks like JobRule1 takes a constructor argument which isn't allowed for the quartz API (which must have a zero-arg default constructor class type extending Job. Or am I missing the point and is the implicit object taking care of that? – Albert May 07 '12 at 14:29
  • I did not know of this restriction in quartz API (I defined my own classes so it compiles). I think my proposed implementation consists of too much code, maybe I can improve it (and get rid of the constructor argument). – Christian May 07 '12 at 14:33
  • FYI: the quartz jobBuilder factory method: public static JobBuilder newJob(Class extends Job> jobClass) (thanks btw so far!) – Albert May 07 '12 at 14:37
  • Yeah, this works and is nice and clean as well. I'm still wondering though if it isn't be possible for the GenericJobRule to 'instantiate' an instance of J since it doesn't have any constructor args anyway without losing typesafety. Thanks, I'll flag this as an answer! – Albert May 07 '12 at 18:53
  • Here is some more information about instantiation of abstract types, could be helpful: http://stackoverflow.com/questions/818996/abstract-types-type-parameters-in-scala – Christian May 09 '12 at 16:19