Monday, 23 June 2008

The Quartz Plugin

The docs for the quartz plugin are more than adequate for installation and usage, but they don't give you any help how to test. The first step is to make sure quartz is stopped on your test machines, otherwise jobs will fire at random during a test run and breaking things.

You do this by editing QuartzConfig.groovy and setting autoStartup=false for your test / dev environments.

Next I added a quartz action to our FixtureController. The job name is simply the class name.

class FixtureController {
def quartzScheduler

def quartz = {
if (params.triggerJob) {
quartzScheduler.start()
quartzScheduler.triggerJob(params.triggerJob, 'GRAILS_JOBS')
} else if (params.standby) {
quartzScheduler.standby()
}
render("<pre>${quartzScheduler.metaData}</pre>")
}
}


You can now trigger your job by hitting http://server:port/fixture/quartz?triggerJob=MyJob

The quartz jobs are (obviously) asynchronous, so your test harness will need some way of checking when they've started, and when they are finished. In the past we used a status page with a meta-refresh, that rendered the word 'continue' when some condition had been met. Once you know the job has started, set quartz back into standby in order to minimise the risk of other jobs kicking off half way through your tests.

2 comments:

Agile Enforcer said...

We've had some problems with Quartz jobs not running - or in some cases stopping running after some period of time.

The problem with Quartz jobs not starting was with on older version of Quartz, and was caused by an injection dependency not being fulfilled when the Quartz job was initialised. This was fixed by an upgrade.

The second problem - a job that was running regularly just stopping - is still under investigation. We suspect it might be a runtime exception caused at a transaction boundary but we've not being able to replicate so for the moment we've increased the logging to get a clue what is going on.

Stephen Cresswell said...

I seem to remember that Quartz unschedules the job if it throws an exception (maybe there's a setting to disable this). If your job is transactional and hibernate throws an exception on session flush the try / catch block in your execute() method won't be any use. You may need to set transaction = false and create a transaction excplicitly using

class MyJob {
 def transacitonal = false
 execute() {
  try {
   DomainObject.withTransaction { status ->
    // Do stuff
   }
  }
 catch (Exception e) {
   // log it
  }
 }
}