Grails Cookbook - A collection of tutorials and examples

Grails Quartz Plugin Tutorial

Grails Quartz plugin allows to execute scheduled business logic on the background using a specified interval or cron expression. This tutorial will show how to install and use this plugin in your Grails applications.

Overview

It is very common for a business application to execute a scheduled task on the background with specific time pattern. For example, if you have a busy online store, you may want to archive all transactions daily at exactly 5pm. Another example is sending emails of business statistics summary to owners and senior managers.
This is very easy to do if you are in a Unix or Unix like environment through execution of batch scripts and scheduling them via cron. But Grails can also do this by using the Quartz plugin. And you don't loose the advantages of Grails' awesomeness.

Install Quartz Plugin

The first step is to install the plugin by editing grails-app/conf/BuildConfig.groovy. Add entry similar to below in the plugins section:
grails.project.dependency.resolution = {
    ...
    plugins {
        ...
        compile ':quartz:1.0.2'
    }
}
You may want to run your Grails application using grails run-app on command line for Grails to download the dependencies.

Creating A Job

After installing the plugin, it is easy to create a job using the create-job script:

grails create-job asia.grails.sample.ArchiveTables

This will create the file PROJECT_ROOT/grails-app/jobs/asia/grails/sample/ArchiveTablesJob.groovy with these contents:
package asia.grails.sample

class ArchiveTablesJob { static triggers = { simple repeatInterval: 5000l // execute job once in 5 seconds } def execute() { // execute job } }

The code inside the execute method are called at the intervals defined in the triggers section.

Grails Features

Many of the things that you can do inside a controller or service can be done inside a Job. In you have a service like this:

class PersonService {
    def listTallPeople() {
        return Person.executeQuery("from Person where cmHeight >= 182")
    }
}

You can use the service inside a Job, and so much more:

class UpdatePeopleJob {
    def personService
    static triggers = {
      simple repeatInterval: 5000l // execute job once in 5 seconds
    }
    def execute() {
        println "Hi, Archive Job now running"
        def tallPeople = personService.listTallPeople()
        tallPeople.each { tallPerson ->
            tallPerson.tallFlag = true
            tallPerson.save()
        }
        println "Bye, Archive Job is done now"
    }
}

Since a Job is a Groovy class, you can use Groovy language features inside.

Scheduling

The only tricky part is configuring when the jobs should be run. Here are the different methods:

Simple Trigger
Simple triggers are used if you want to schedule a job using a pre-defined interval. All parameters on simple triggers are interpreted as milliseconds.

This example will execute immediately when a Grails application starts. And then it will be repeatedly invoked every 5 seconds.

    static triggers = {
      simple repeatInterval: 5000l // execute job once in 5 seconds
    }
}

This example will execute 60 seconds after a Grails application starts. And then it will be repeatedly invoked every 15 seconds.

    static triggers = {
      simple startDelay:60000, repeatInterval: 15000l // execute job once in 5 seconds
    }

The problem with simple triggers is you can't control the exact time a job will run. For example, you can create a job to run hourly (repeatInterval: 3600000l) but you can't set it to run at every 15 minute mark of every hour. The timing all depends on what time the first run started.
Cron Trigger
Cron trigger is a more flexible way of scheduling jobs. You can control the times a job will run by matching with a cron expression. Here are the fields:
cronExpression: "s m h D M W Y"
                 | | | | | | `- Year [optional]
                 | | | | | `- Day of Week
                 | | | | `- Month
                 | | | `- Day of Month
                 | | `- Hour
                 | `- Minute
                 `- Second
Specific values, list or wild-cards are allowed. But understanding cron expressions are best learned through examples:

  • Fire every five minutes. It should fire on 0, 5, 10 ... 55 minute mark
        static triggers = {
          cron name: 'myTrigger', cronExpression: "0 0/5 * * * ?"
        }
    
  • Fire every hour
        static triggers = {
          cron name: 'myTrigger', cronExpression: "0 0 * * * ?"
        }
    
  • Fire every 2 hours. It should fire on the hours 0, 2, 4 ... 22
        static triggers = {
          cron name: 'myTrigger', cronExpression: "0 0 0/2 * * ?"
        }
    
  • Fire every 10 minutes. It should fire on the minutes 0, 10, 20 ... 50
        static triggers = {
          cron name: 'myTrigger', cronExpression: "20 0/10 * * * ?"
        }
    
  • Fire every half an hour between 8AM AM to 12PM
        static triggers = {
          cron name: 'myTrigger', cronExpression: "0 0/30 8-12 * * ?"
        }
    
  • Fire every tuesday at 12:15pm
        static triggers = {
          cron name: 'myTrigger', cronExpression: "0 15 12 ? * TUE"
        }
    
  • Fire everyday at 1:30am
        static triggers = {
          cron name: 'myTrigger', cronExpression: "0 30 1 ? * MON-SUN"
        }
    
Custom Trigger
The two previous triggers are usually enough for most business scenarios. If you have a more complicated requirement, you can implement your own trigger by implementing the org.quartz.Trigger interface.
Here is a simple example that fires up a job every 5 seconds using custom triggers:

PROJECT_ROOT/grails-app/jobs/asia/grails/sample/SampleJob.groovy

package asia.grails.sample
import asia.grails.sample.triggers.MyCustomTrigger
class SampleJob {
    static triggers = {
        custom name:'customTrigger', triggerClass:MyCustomTrigger
    }
    def execute() {
        println "${new Date()} Hello World"
    }
}

PROJECT_ROOT/src/groovy/asia/grails/sample/triggers/MyCustomTrigger.groovy

package asia.grails.sample.triggers
import org.quartz.impl.triggers.CalendarIntervalTriggerImpl
class MyCustomTrigger extends CalendarIntervalTriggerImpl{
    public MyCustomTrigger() {
        repeatInterval = 1
    }
    @Override
    public Date computeFirstFireTime(Calendar calendar) {
        calendar.add(Calendar.SECOND, 5)
        return calendar.getTime()
    }
    @Override
    public Date getNextFireTime() {
        Calendar calendar = Calendar.getInstance()
        calendar.add(Calendar.SECOND, 5)
        return calendar.getTime()
    }
}

CalendarIntervalTriggerImpl is a class that indirectly implements the Trigger interface.
Custom triggers is a bit complex as the Trigger interface has a lot of methods to be implemented. The example above is not a good example because it is very simple one that just passes compilation and run time errors.

Note

In my experience of using this plugin, I found issues where my jobs are executed twice at the same time. For example, I have a job that should run at 12:15pm every day via cron expression. And there are certain weeks where the job runs twice at 12:15pm. But there are weeks where it runs perfectly. I suggest you research more about issues of this plugin if you plan to use it with critical tasks.

Other Grails Plugins Tutorials


Tags: Plugin, quartz