Shalvika Sood

Scheduling Jobs Dynamically with Grails

Ever since I started using Grails a few months ago, I have become a strong advocate of it. However, as I inhale freely in an uncluttered world of convention over configuration and feel secure in the familiarity of the surroundings (I come from a Java background), I can’t help but notice the lack of proper sign boards on the way. Yes, my grouch is with the documentation. It’s pretty good to do basic things, but as the complexity increases, information starts becoming scarce and mining the search engines becomes a must.

A few weeks ago, our team faced a challenge in utilizing the power of the quartz plugin (version 0.4.2).

The Problem – Schedule some tasks dynamically at a user defined time

The Probable Solution – Use the dynamic scheduling methods of the quartz plugin (available since 0.4.1 version)

The Stumbling Block – The code wouldn’t work as per the official documentation.

‘What’s new in that!’, you would say and I completely agree.  So we did  what any self-respecting software engineer whose burnt his or her hands with ‘integrations’ would do – got out our spades and dug through the rubble (and ramble) on blogs & user-forums (grails-user,stackoverflow) to find that the missing link was a small innocuous line.

The plugin documentation states very clearly that there are 4 methods available for scheduling jobs dynamically

  • 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 that can be used to pass data to the job. To execute the job, all that is required to be done is

class MyJob {

def execute(context) {

println context.mergedJobDataMap.get(‘data’)
}

}

In the controller class

MyJob.schedule(datetime, [myData:”Thank you for posting”])

However, simply giving the job class as above fires the job at the default plugin interval. For it to trigger on the date parameter specified, an empty triggers block needs to be declared in the job class. So the job class will look like:

class MyJob {

static triggers ={}

def execute(context) {

println context.mergedJobDataMap.get(‘myData’)
}

}

One thing to remember is that to invoke the job from the controller, you have to import the package of the job class, otherwise grails throws an exception of ‘no such property MyJob for MyController’

Why I include the package and not just the job class? Because then grails doesn’t recognize the overloaded schedule() method.

That’s it! You’re good to go!

Scroll down if you want to know actual steps and code to create a utility which sends emails at a user specified time

//Controller Class

package com.appName

import com.appName.email.*; -> Package of the job class

class EmailController {

def scheduleEmail ={

def dateTime = params.dateTime
SimpleDateFormat dateFormat = new SimpleDateFormat(“MM/dd/yyyy hh.mm a”)
def scheduledDate = dateFormat.parse(dateTime)  -> Ensure that the time is in the required format, else method will throw an unparsable date exception

ScheduleEmailJob. schedule(scheduledDate, params) -> Calling the method to schedule a job dynamically . Since it takes a map object as an optional parameter, we are sending the params object directly which has the entire user input

render message(code:”email.scheduleEmail.success”)

}

}

To create a job class, run the command create-job giving the package(optional) and classname(required). This creates a new job in the grails-app/jobs/package directory.

create-job com.appName.email.ScheduleEmail

//Job Class

package com.appName.email

import org.quartz.JobExecutionContext; -> Required to import for using the job context to get user data
import
org.quartz.JobExecutionException; -> Optional. It’s a good idea to throw this exception
import
org.grails.plugins.quartz.JobManagerService;

class ScheduleEmailJob {

def emailService -> Dependency injection for the Email Service which is invoked from the job    class to do the actual processing. The plugin allows to access service classes using DI

/**Static block which has to be declared so that the job is executed at the time specified in the schedule method**/

static triggers = {}
def
execute(JobExecutionContext context) throws JobExecutionException {

try{

log.debug(“Job has been called, sending mail”);
//Call the sendEmail method in the email service

emailService.sendEmail(context.mergedJobDataMap)

}catch (Throwable e) {

throw new JobExecutionException(e.getMessage(), e);

}

}

}

The service class uses the grails mail plugin to send the emails. (Configuring a mail plugin is pretty simple. The documentation for the grails mail plugin works!)

//Service class

class EmailService {

def mailService -> Dependency injection for service class in Mail plugin
def
sendEmail(Map paramsMap){

def emailSubject = paramsMap.subject
def
recipientToList = paramsMap.recipientToList
def
message = paramsMap.emailMessage
def
recipientCCList = paramsMap.recipientCcList
def
recipientBCCList = paramsMap.recipientBccList

//Converting the recipient lists to arrays for multiple recipients

String[] recipientToArray = recipientToList.toArray()
String[] recipientCCArray = recipientCCList.toArray()
String[] recipientBCCArray = recipientBCCList.toArray()

try{

mailService.sendMail {

if(emailSubject)

subject emailSubject

if(recipientToList)

to recipientToArray

if(recipientCCList)

cc recipientCCArray

if(recipientBCCList)

bcc recipientBCCArray

html message

}

return true

}catch (Exception mailSenderException){

log.error(“Exception in sending mail        “+mailSenderException.getMessage())

return false

}

}

Voila – You’ve Got Mail!!

 

Related Articles

#Tech

NHibernate, Linq and Lazy Collections

For the past few months we have been busy building a services framework for one of our clients so that they can build all of their enterprise web services without ever having to worry about the cross cutting concerns and... Read more
#Tech

Page Redirects using Spring.NET

Who is responsible for page redirects in ASPNET MVP – The View or the Presenter None of the above, it is you :) On a serious note, it is the View as one shouldn’t pollute the Presenter with the page navigation... Read more