Login required
Download

Quartz Plugin

(33)
Used by approximately
15%
of Grails users
Author(s) Sergey Nebolsin
Current Release 0.4.2   (1 year ago)
Grails Version 1.1 > *
Tags jobs  scheduling  utility 
Dependency
compile ":quartz:0.4.2"
Quartz plugin allows your Grails application to schedule jobs to be executed using a specified interval or cron expression. The underlying system uses the Quartz Enterprise Job Scheduler configured via Spring, but is made simpler by the coding by convention paradigm.
Last updated by rlovtang 5 months ago
Just execute following from your application directory:
grails install-plugin quartz
If above method does not work for some reason you can install it with:
grails install-plugin http://cloud.github.com/downloads/nebolsin/grails-quartz/grails-quartz-0.4.2.zip

If you're interested in the latest sources, visit GitHub project page or use

git clone git://github.com/nebolsin/grails-quartz.git
Last updated by rlovtang 5 months ago
Quartz plugin allows your Grails application to schedule jobs to be executed using a specified interval or cron expression. The underlying system uses the Quartz Enterprise Job Scheduler configured via Spring, but is made simpler by the coding by convention paradigm.

Grails-Quartz 0.4.2 is the current stable version. Grails-Quartz 1.0 is the latest development snapshot.

See JIRA and GitHub Issues for known issues.

See GitHub project page for the latest sources.

Using

Scheduling Jobs

To create a new job run the "grails create-job" command and enter the name of the job. Grails will create a new job and place it in the "grails-app/jobs" directory:

class MyJob {
  static triggers = {
    simple name: 'mySimpleTrigger', startDelay: 60000, repeatInterval: 1000  
  }

def group = "MyGroup"

def execute(){ print "Job run!" } }

The above example will wait for 1 minute and after that will call the 'execute' method every second. The 'repeatInterval' and 'startDelay' properties are specified in milliseconds and must have Integer or Long type. If these properties are not specified default values are applied (1 minute for 'repeatInterval' property and 30 seconds for 'startDelay' property). Jobs can optionally be placed in different groups.

The triggers name property must be unique across all triggers in the application.

By default, jobs will not be executed when running under the test environment.

Scheduling a Cron Job

Jobs can be scheduled using a cron expression. For those unfamiliar with "cron", this means being able to create a firing schedule such as: "At 8:00am every Monday through Friday" or "At 1:30am every last Friday of the month". (See the API docs for the CronTrigger class in Quartz for more info on cron expressions):

class MyJob  {
  static triggers = {
    cron name: 'myTrigger', cronExpression: "0 0 6 * * ?"
  }

def group = "MyGroup"

def execute(){ print "Job run!" } }

The fields in the cronExpression are: (summarizing the Quartz CronTrigger Tutorial)

cronExpression: "s m h D M W Y"
                 | | | | | | `- Year [optional]
                 | | | | | `- Day of Week, 1-7 or SUN-SAT, ?
                 | | | | `- Month, 1-12 or JAN-DEC
                 | | | `- Day of Month, 1-31, ?
                 | | `- Hour, 0-23
                 | `- Minute, 0-59
                 `- Second, 0-59
Note:
  • Year is the only optional field and may be omitted, the rest are mandatory
  • Day-of-Week and Month are case insensitive, so "DEC" = "dec" = "Dec"
  • Either Day-of-Week or Day-of-Month must be "?", or you will get an error since support by the underlying library is not complete. So you can't specify both fields, nor leave both as the all values wildcard "*"; this is a departure from the unix crontab specification.
  • See the CronTrigger Tutorial for an explanation of all the special characters you may use.

Multiple Triggers for a Job

You can also configure multiple triggers per job as well as register your custom trigger.

class MyJob {
    static triggers = {
        simple name:'simpleTrigger', startDelay:10000, repeatInterval: 30000, repeatCount: 10
        cron name:'cronTrigger', startDelay:10000, cronExpression: '0/6 * 15 * * ?'
        custom name:'customTrigger', triggerClass:MyTriggerClass, myParam:myValue, myAnotherParam:myAnotherValue
    }

def execute() { println "Job run!" } }

With this configuration job will be executed 11 times with 30 seconds interval with first run in 10 seconds after scheduler startup (simple trigger), also it'll be executed each 6 second during 15th hour (15:00:00, 15:00:06, 15:00:12, … this configured by cron trigger) and also it'll be executed each time your custom trigger will fire.

Three kinds of triggers are supported with the following parameters:

  • simple :
    • name the name that identifies the trigger
    • startDelay delay (in milliseconds) between scheduler startup and first job's execution
    • repeatInterval timeout (in milliseconds) between consecutive job's executions
    • repeatCount trigger will fire job execution (1 + repeatCount) times and stop after that (specify 0 here to have one-shot job or -1 to repeat job executions indefinitely)
  • cron :
    • name the name that identifies the trigger
    • startDelay delay (in milliseconds) between scheduler startup and first job's execution
    • cronExpression cron expression
  • custom :
    • triggerClass your class which implements Trigger interface
    • any params needed by your trigger

Dynamic Jobs Scheduling

Starting from 0.4.1 version you have the ability to schedule job executions dynamically.

These methods are available:

  • MyJob.schedule(String cronExpression, Map params?) — creates cron trigger;
  • MyJob.schedule(Long repeatInterval, Integer repeatCount?, Map params?) — creates simple trigger: repeats job repeatCount+1 times with delay of repeatInterval milliseconds;
  • MyJob.schedule(Date scheduleDate, Map params?) — schedules one job execution to the specific date;
  • MyJob.schedule(Trigger trigger) — schedules job's execution with a custom trigger;
  • MyJob.triggerNow(Map params?) — force immediate execution of the job.
Each method (except the one for custom trigger) takes optional 'params' argument. You can use it to pass some data to your job and then access it from the job:

class MyJob { 
  def execute(context) {
    println context.mergedJobDataMap.get('foo')
  }
}

// now in your controller (or service, or something else):

MyJob.triggerNow([foo:"It Works!"])

Dependency Injection and Jobs

Jobs are configured for auto-wiring by name thus properties can be injected into them. To get hold of the data source you can do:

def dataSource
Or a service class:
def myService
The Grails application context is configured using convention so most types can be injected simply by using their property name representation (ie the first letter being lower case in the majority of cases).

Using JobExecutionContext

Since 0.3.2 you could define execute method in your job as def execute(context){} in this case it will be provided with Quartz's JobExecutionContext. You could obtain some information from this context (name of the trigger which fired the execution, previous execution time, next execution time and so on) as well as provide some parameters to job through context.getMergedJobDataMap() . If your job is stateful (see section about concurrent execution below), job data in JobExecutionContext will also persist per job executions.

Configuring the plugin

Since 0.3 version plugin supports configuration file which is stored in grails-app/conf/QuartzConfig.groovy . The syntax is the same as default Grails configuration file Config.groovy . You could also use per-environment configuration feature (more info).

To have an initial Quartz config file generated for you, type the following in the command line: 'grails install-quartz-config' . This will generate a file that looks like this:

quartz {
    autoStartup = true
    jdbcStore = false
}

environments { test { quartz { autoStartup = false } } }

Currently supported options:

  • autoStartup controls automatic startup of the Quartz scheduler during application bootstrap (default: true )
  • jdbcStore set to true if you want Quartz to persist jobs in your DB (default: false ), you'll also need to provide quartz.properties file and make sure that required tables exist in your db (see Clustering section below for the sample config and automatic tables creation using Hibernate)
You could also create grails-app/conf/quartz.properties file and provide different options to the Quartz scheduler (see Quartz configuration reference for details).

Logging

A log is auto-injected into your task Job class without having to enable it. To set the logging level, just add something like this to your grails-app/conf/Config.groovy log4j configuration.

debug 'grails.app.jobs'

Hibernate Sessions and Jobs

Jobs are configured by default to have Hibernate Session bounded to thread each time job is executed. This is required if you are using Hibernate code which requires open session (such as lazy loading of collections) or working with domain objects with unique persistent constraint (it uses Hibernate Session behind the scene). If you want to override this behavior (rarely useful) you can use 'sessionRequired' property:

def sessionRequired = false

Configuring concurrent execution

By default Jobs are executed in concurrent fashion, so new Job execution can start even if previous execution of the same Job is still running. If you want to override this behavior you can use 'concurrent' property, in this case Quartz's StatefulJob will be used (you can find more info about it here):

def concurrent = false

Clustering

Quartz plugin doesn't support clustering out-of-the-box now. However, you could use standard Quartz clustering configuration. Take a look at the example provided by Burt Beckwith: Quartz Plugin clustering_sample.tar.gz. You'll also need to set jdbcStore configuration option to true .

There are also two parameters for configuring store/clustering on jobs ( volatility and durability , both are true by default) and one for triggers ( volatility , also true by default). Volatile job and trigger will not persist between Quartz runs, and durable job will live even when there is no triggers referring to it.

Read Quartz documentation for more information on clustering and job stores as well as volatility and durability.

Recovering

Since 0.4.2 recovering from 'recovery' or 'fail-over' situation is supported with requestsRecovery job-level flag ( false by default).

If a job "requests recovery", and it is executing during the time of a 'hard shutdown' of the scheduler (i.e. the process it is running within crashes, or the machine is shut off), then it is re-executed when the scheduler is started again. In this case, the JobExecutionContext.isRecovering() method will return true.

Testing

Currently QuartzScheduler is turned off in the test environment. You could override this behavior by setting autoStartup property for test environment in your QuartzConfig or by starting up the scheduler in concrete test cases using Scheduler#start().

Compatibility with Grails versions

Quartz Plugin 1.0-SNAPSHOT — works with Grails 1.3

Quartz Plugin 0.4.2 tested to work with Grails 1.2

Quartz Plugin 0.4.x tested to work with Grails 1.1 and Grails 1.1.1

Quartz Plugin 0.3.x tested to work with Grails 1.0.3

Quartz Plugin 0.2 works with Grails 0.6+

In earlier version of Grails (before 0.5.5) job sheduling with Quartz was a part of Grails core, but as of 0.5.5 it was moved out of core to this plugin. Use this plugin with Grails 0.6 or higher.

Roadmap

1.0 version (December 2010)

  • support configurable trigger listeners and job listeners
  • support Quartz clustering and store configuration out-of-the-box

Known issues

You can find a list of known issues in the JIRA and on the GitHub.

Last updated by 000panther 2 months ago
Q: Can I change the scheduling for a Quartz job in Grails dynamically?

I'd like to have a cron style processing routine check for work at 60 second intervals, but have it consult a database parameter for a new time interval. If it's different than 60 seconds, I'd like the cron schedule changed to the new value. That way I can dynamically tune the frequency without restarting the application by updating the database value. So far I've only been able to add more cron type triggers, but not replace the existing trigger.

Q: How to run a Quartz job in a specific timezone?

A Quartz job can be run in a specific timezone by adding a timeZone Parameter to the jobs trigger. Example:

static triggers = {
  // daily at 10pm PST
  cron name: 'cronPstTrigger', cronExpression: "0 0 22 * * ?", timeZone: TimeZone.getTimeZone("PST")
}
Last updated by admin 2 years ago