Thursday, October 1, 2009

Quartz Scheduler with Spring

Spring and Quartz Scheduler:

We will take an example of sending the reminder of timesheet submission every Friday at 1:00 PM to all the employees using Quartz Scheduler:
Quartz Scheduler mainly uses the following 3 for implementing the job scheduling using spring:

1. JobDetailBean
2. CronTriggerBean
3. SchedulerFactoryBean
Let us see the role of each of them in detail:

JobDetailBean: JobDetailBean is simply a JavaBean representation of a JobDetail. JobDetail can be considered something whose objects contain all the information needed to run a Job. i.e. JobDetailsBean is a subclass of Quartz’s org.quartz.JobDetail, which requires the Job implementation be set through ‘jobClass' property. Let’s see how we configure a JobDetailBean in our context file:

<bean name="timsheetReminderJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.prokarma.TimeSheetReminderJob"/>
<property name="jobDataAsMap">
<map>
<entry key="timesheetServive" value-ref="timesheetService"/>
</map>
</property>
</bean>

And here is the TimeSheetReminderJob class which is declared above:

public class TimeSheetReminderJob extends QuartzJobBean {

private TimesheetService timesheetService;

/**
* Setter called after the TimesheetReminderJob is instantiated
* with the value from the JobDetailBean
*/
public void setTimesheetService(TimesheetService timesheetService) {
this.timesheetService = timesheetService;
}

protected void executeInternal(JobExecutionContext ctx)
throws JobExecutionException {
// do the actual work i.e. send timesheetRemiders
timesheetService.sendTimesheetRemiders();
}
}


As can be seen from the bean declaration and the TimeSheetRemiderClass, We have configured a bean named as ‘timeSheetReminder’ as a JobDetailBean. The properties of this bean are very important to understand. 'jobClass' property specifies which class will act as a job (In this case, TimeSheetReminderJob) and hence run. This class should extend QuartzJobBean and implement the method executeExternal which contains the business logic that should be implemented for the job (in this case, sending timesheet reminders by calling sendTimeSheetRemider() method form it). The jobDataAsMap’ property takes a java.util.Map that contains properties that are to be set on the object specified by the jobClass (Since, we have just 1 property ‘timeSheetService’, we set it and give it a ref to another bean somewhere defined in the context file.

CronTriggerBean: Now, since the job is created, we need to specify when this job should run (In our case, as we said earlier, we need it to run every Friday at 1:00 PM). This is specified through org.quartz.Trigger class. There are 2 implementation of Trigger class. They are SimpleTriggerBean and CronTriggerBean. Using SimpleTriggerBean, we can just specify how often a job should run and (optionally) how long to wait for the job to run for the first time (i.e. after the context startup). Here we cannot specify inner details like the exact day/date/month/year etc and exact time on which it should run. Hence, we opted for CronTriggerBean as our Trigger for our TimeSheetReminderJob and specify the exact date and time for the job.
Here is how we configure the CronTiggerBean as a bean in our context file:

<bean id="cronTriggerBean" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="timesheetReminderJob"/>
<!-- run every Friday at 1 PM -->
<property name="cronExpression" value="0 0 13 ? * FRI"/>
</bean>


As can be seen, we just need to set 2 properties for the CronTriggerBean. They are: jobDetail (which specifies the JobDetailBean we declared earlier i.e. which specifies the class which acts as a job) and ‘cronExpressionProperty’ which specifies a expression that identifies the runtime of a job. It can be broken upto six (or possibly seven) time elements, separated by spaces. In order from left to right, these elements are defined as follows:

1. Seconds (0-59)
2. Minutes (0-59)
3. Hours (0-23)
4. Day of month (1-31)
5. Month (1-12 or JAN-DEC)
6. Day of Week (1-7 or SUN-SAT)
7. Year (1970-2099)


Each of these elements can be specified with an explicit value (eg. 6), a range (ex: 9-12), a list (eg. 9,10,11) or a wildcard (eg. *). The day of the month and day of the week are mutually exclusive, so you should specify just one of those fields by specifying the other field as a blank (i.e. a ‘?’)
In the case of our cronTriggerBean, we’ve set the cronExpression as “0 0 13 ? * FRI”. You can read this as “Zeroth second of 0th minute of 1 PM (13:00 in 24hrs format is 1:00 PM ) on any day of the month of any month and every Friday of the week”. Some examples of cron expressions and their meanings are specified below:

0 0 10,14,16 * * ? Every day at 10AM, 2PM and 4PM
0 0,15,30,45 * 1-30 ? Every 15 minutes on the first 30 days of the month
30 0 0 1 1 ? 2012 30 seconds after midnight on Jan 1, 2012
0 0 8-17 ? * MON-FRI Every Monday to Friday from 8AM to 5PM

SchedulerFactoryBean: To start a Quartz’s job, we will use Spring’s SchedulerFactoryBean. It is declared as:

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTriggerBean"/>
</list>
</property>
</bean>

The triggers’ property takes an array of references to trigger beans. Since we only have a single trigger at the moment, we simply need to wire it with a list containing a single reference to the cronTriggerBean bean.
This was the final step in setting up the Quartz scheduler in Spring. At this time, our timesheet reminder service will be calling sendTimesheetRemiders() menthod on every Friday at 1 PM.