What is Quartz Scheduler? Quartz is a full-featured, open source job scheduling framework written entirely in Java. It can be integrated with, or used along side virtually any J2EE or J2SE application - from the smallest stand-alone application to the largest e-commerce system. Job scheduler is a system that is responsible for executing other software components when a pre-determined (scheduled) time arrives. Quartz is quite flexible. It contains multiple usage paradigms that can be used separately or together to achieve the desired behavior. And it enables you to write the code in the way that seems most natural to your project. Quartz is a fault-tolerant, and can persist your scheduled jobs between system shutdown and restarts. Quartz is very useful for simply executing certain system process on given schedules. What is Quartz - From a Software Component View? Quartz contains all of the core Quartz functionality and it is distributed as a small java library (.jar file). Scheduler interface is the main interface (API) to this functionality. It provides some simple operations like scheduling jobs, un-scheduling jobs, starting/stopping/pausing the scheduler. If you want to schedule you software components then it must implement the Job interface which override the execute() method. If you want components have to notify when scheduled fire-time arrives, then you should implements either the TriggerListener or JobListener interface. Why Quartz Scheduler? Quartz Scheduler can be integrated with, or used along side virtually any J2EE or J2SE application - from the smallest stand-alone application to the largest e-commerce system. Quartz scheduler is a fault-tolerant, and can persist you scheduled jobs between system shutdown and restarts. It contains the all core Quartz functionality. Once you configure the Job class and set up the schedule, Quartz will take care of the rest. When the Scheduler determines the notifying time to your job, the Quartz framework will call the execute() method on your Job class. You don't have to report anything back to the Scheduler. If you configure your Job to be called again at a later time, the framework will take care of calling again at the right time. In the next section you will learn how to download Quartz Job Scheduler and then configure the development environment in eclipse. In this section we will download Quartz Job Scheduler from its distribution web site and then create development environment in the eclipse integrated development environment. Downloading Quartz Job Scheduler Quartz is an open source enterprise grade software for the development of Job Scheduling application in java. Quartz Scheduler is from OpenSymphony which can be downloaded from http://www.opensymphony.com/. At the time of writing this tutorial the latest version of Quartz Scheduler is 1.6.0. You can download the latest version of software and then try. For this tutorial we have downloaded quartz-1.6.0.zip. After downloading the zip file extract it and you will find the following directory in the extracted directory. Here is the description of files and directories: Files/Directory Purpose quartz-all-<ver>.jar Quartz library includes the core Quartz components and all optional packages. If you are using this library then no other quartz-*.jars need to include. quartz-<ver>.jar quartz-jboss-<ver>.jar quartz-oracle-<ver>.jar quartz-weblogic-<ver>.jar build.xml docs docs/wikidocs docs/dbTables src/java/org/quartz src/java/org/quartz/core src/java/org/quartz/simpl core Quartz library. optional JBoss specific Quartz extensions such as the Quartz startup MBean, QuartzService. optional Oracle specific Quartz extensions such as the OracleDelegate optional WebLogic specific Quartz extensions such as the WebLogicDelegate an "ANT" build file, for building Quartz. root directory of all documentation the main documentation for Quartz. Start with the "index.html" sql scripts for creating Quartz database tables in a variety of different databases. the main package of the Quartz project, containing the 'public' (client-side) API for the scheduler a package containing the 'private' (server-side) components of Quartz. this package contains simple implementations of Quartz support modules (JobStores, ThreadPools, Loggers, etc.) that have no dependencies on external (third-party) products. src/java/org/quartz/impl src/java/org/quartz/utils src/examples/org/quartz webapp lib this package contains implementations of Quartz support modules (JobStores, ThreadPools, Loggers, etc.) that may have dependencies on external (thirdparty) products - but may be more robust. this package contains some utility/helper components used through-out the main Quartz components. this directory contains some examples usage of Quartz. this directory contains a simple web-app for managing Quartz schedulers. this directory contains all third-party libraries that are needed to use all of the features of Quartz. Setting Up Development project in Eclipse Run the eclipse IDE and create new java project. From the file menu select --> New --> Project New Project dialog box displayed as follows: Select "Java Project" and click on the "Next >" button. Enter "Quartz Project" in the Project Name text box and click on the "Finish" button to create the project. A new project is created and following screen is displayed. Adding required quartz libraries to the project Copy the lib directory and all the jar files from the unzipped directory and then paste into the eclipse project (Ctrl +C and Ctrl +V). . Open the project property and then add all the jar files into the build path as shown below. and then click on the "OK" button. Now your development environment is ready for the development. In the next section we will develop a simple scheduler application that prints "Hello Quartz Scheduler" on the console at particular time interval. In this section we are going to develop a simple Quartz Scheduler application with the help of Quartz framework. That will display "Hello World Quartz Scheduler :<date & time>" on the console window after specified time schedule. Before using Scheduler you have to instantiate it. For doing this some users may keep an instance of a factory serialized in a JNDI store, and some other users may find it just to instantiate and use a factory instance. Firstly create the instance of SchedulerFactory by getting the reference of org.Quartz.impl.StdSchedulerFactory Class. Invoke the getScheduler() method by this instance to instantiate the Scheduler. Description of code: execute(): Any software components you want to schedule then you must implement the Job interface and override it execute() method. JobExecutionContext: The JobExecutionContext object that is passed to execute() method provides the job instance with information about its "run-time" environment - a handle to the Scheduler that executed it, a handle to the Trigger that triggered the execution, the job's JobDetail object, and a few other items. SchedulerFactory: SchedulerFactory is a interface provides a mechanism for obtaining clientusable handles to Scheduler instances. StdSchedulerFactory(): A Class StdSchedulerFactory is a class and it is implementation of SchedulerFactory interface. Here it just using for create an instance of SchedulerFactory instance. Scheduler: Scheduler interface is the main interface (API) to this functionality. It provides some simple operations like scheduling jobs, unscheduling jobs, starting/stopping/pausing the scheduler. getScheduler(): SchedulerFactoy interface having the getScheduler() method that returns an instance of Scheduler. start(): This method is used to starts the Scheduler's threads that fire Triggers. At the first time when we create the Scheduler it is in "stand-by" mode, and will not fire triggers. The scheduler can also be send back into stand-by mode by invoking the standby() method. JobDetail(String name, String group, Class jobclass): The JobDetail object is created at the time the Job is added to scheduler. It contains various property settings like job name, group name and job class name. It can be used to store state information for a given instance of job class. SimpleTrigger(String name, String group, Date startTime, Date endTime, int repeatCount, long repeatInterval): Trigger objects are used to firing the execution of jobs. When you want to schedule the job, instantiate the trigger and set the properties to provide the scheduling. DEFAULT_GROUP: It is a constant, specified that Job and Trigger instances are belongs to which group.. REPEAT_INDEFINITELY: It is a constant used to indicate the 'repeat count' of the trigger is indefinite. scheduleJob(JobDetail jd, SimpleTrigger st): This method is used to add the JobDetail to the Scheduler, and associate the Trigger with it. Here is the code of Job Class: import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.Date; public class HelloJob implements Job { public void execute(JobExecutionContext arg0) throws JobExecutionException{ System.out.println("Hello World Quartz Scheduler: " + new Date()); } } Download this code Here is the code of Scheduler Class: import java.util.Date; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.SimpleTrigger; import org.quartz.impl.StdSchedulerFactory; public class HelloSchedule { public HelloSchedule()throws Exception{ SchedulerFactory sf=new StdSchedulerFactory(); Scheduler sched=sf.getScheduler(); sched.start(); JobDetail jd=new JobDetail("myjob",sched.DEFAULT_GROUP,HelloJob.class); SimpleTrigger st=new SimpleTrigger("mytrigger",sched.DEFAULT_GROUP,new Date(), null,SimpleTrigger.REPEAT_INDEFINITELY,60L*1000L); sched.scheduleJob(jd, st); } public static void main(String args[]){ try{ new HelloSchedule(); }catch(Exception e){} } } Download this code Output of the program : log4j:WARN No appenders could be found for logger (org.quartz.simpl.SimpleThreadPool). log4j:WARN properly. Please initialize the log4j system Hello World Quartz Scheduler: Fri Feb 16 16:15:10 GMT+05:30 2007 Hello World Quartz Scheduler: Fri Feb 16 16:16:10 GMT+05:30 2007 Hello World Quartz Scheduler: Fri Feb 16 16:17:10 GMT+05:30 2007 Hello World Quartz Scheduler: Fri Feb 16 16:18:10 GMT+05:30 2007 Hello World Quartz Scheduler: Fri Feb 16 16:19:10 GMT+05:30 2007 Hello World Quartz Scheduler: Fri Feb 16 16:20:10 GMT+05:30 2007 If you want to schedule you software components then it must implement the Job interface which override the execute() method. Here is the interface: package org.quartz; public interface Job { public void execute(JobExecutionContext context) throws JobExecutionException; } The JobExecutionContext object which is passed to execute() method provides the job instance with information about its "run-time" environment - a handle to the Scheduler that executed it, a handle to the Trigger that triggered the execution, the job's JobDetail object, and a few other items. The JobDetail object is created at the time the Job is added to the scheduler. The JobDetail object various property settings for the Job, as well as a JobDataMap, which can be used to store state information for a given instance of your job class. Trigger objects are used to start the execution of jobs. When you want to schedule a job, you instantiate a trigger and set' its properties to provide the scheduling according to your need. There are currently two types of triggers: SimpleTrigger and CronTrigger. Generally, SimpleTrigger is used if you need just single execution of a job at a given moment in time or if you need to start a job at a given time, and have it repeat N times, with a delay of T between executions. CronTrigger is used when you want to triggering based on calendar-like schedules - such as "every Saturday, at noon" or "at 12:35 on the 15th day of every month." Why Job & Triggers Many job schedulers do not have separate notions of jobs and triggers. Some job Schedulers define a 'job' as simply an execution time with some small job identifier. And some others are much like the combination of Quartz's job and trigger objects. While developing Quartz, we decided that it made sense to create a separation between the schedule and the work to be performed on that schedule. Identifiers Jobs and Triggers are predefined identifying names as they are registered with the Quartz scheduler. You can placed jobs and triggers into 'groups' also, for organizing the jobs and triggers into categories for later maintenance. The name of a job or trigger must be unique within its group. More about Job & JobDetail In this section you will understand some more things about nature of jobs, about the execute() method of the Job interface, and about the JobDetails. While a class implementing the job interface that is the actual 'job', and you have to inform to Quartz about some attributes that you may want job to have. That can be done by the JobDetail class. The JobDetail instance refers to the job to be executed by just providing the job's class. When scheduler executes the job, it creates the new instance of the job class before invoking the execute() method. The jobs must have a no-argument constructor that is the ramification of this behaviour. And it is not useful to define data members on the job class - as their values would be cleared every time job executes. The JobDataMap, which is part of the JobDetail object. It is useful to provide properties/configuration for Job instance and to keep track of a job's state between executions. The JobDataMap can be hold any number of (serializable) objects which you want that available to the job instance when it executes. JobDataMap is an implementation of the Java Map interface, and has some added convenience methods for storing and retrieving data of primitive types Here is a code of putting data into the JobDataMap before adding the job to the Scheduler: jobDetail.getJobDataMap().put("name", jobDetail.getJobDataMap().put("floatValue", "Rose India"); 5.141f); Here is a of getting data from the JobDataMap during the job's execution: JobDataMap databMap = context.getJobDetail().getJobDataMap(); String name = float fvalue = dataMap.getFloat("floatValue"); dataMap.getString("name"); Description of the following example: In the following example we are going to develop a simple Quartz Scheduler application with the help of Quartz framework. To make a Quartz application we need to make two java classes. In one class (job class) we provide the job that we want to execute and in another class (schedule class) we provide the schedule to this class. Job class (NewJob.java) is a implementation of Job interface and it overrides the execute() method that contains the job which we want to execute and throws JobExecutionException. And in schedule class (NewSchedule.java) we just provide the schedule to this job. That program displays a simple message, date, time, job name, group name and float value indefinite times after the specified time. Description of Code: getName() By this method we get the job name that specified in Schedule class in JobDetail properties. ; getGroup() ; By this method we get job group name that specified in Schedule class in JobDetail properties. getJobDataMap() ; This method just return the JobDataMap object. And by JobDataMap object we can invoke the getString() and getFloat() method to retrieve any String or any float value that specified in Schedule class. put() This method is used to just insert the object into the JobDataMap object. Here is the code of Job Class (NewJob.java) : import java.util.Date; import org.quartz.*; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class NewJob implements Job { public void execute(JobExecutionContext jcontext) throws JobExecutionException { System.out.println("Welcome to RoseIndia.Net :"+new Date()); String jname = jcontext.getJobDetail().getName(); String jgroup = jcontext.getJobDetail().getGroup(); System.out.println("job Name :"+jname+" Job Group Name :"+jgroup); JobDataMap jdMap = jcontext.getJobDetail().getJobDataMap(); String name = jdMap.getString("name"); float fval=jdMap.getFloat("floatValue"); System.out.println("Name :"+name+" Float value is :"+fval); } } Download this code Here is code of Schedule Class (NewSchedule.java) : import java.util.Date; import org.quartz.JobDetail; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; import org.quartz.*; public class NewSchedule { public NewSchedule()throws Exception{ SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); JobDetail jd = new JobDetail("myjob","group",NewJob.class); jd.getJobDataMap().put("name", "Rose India"); jd.getJobDataMap().put("floatValue",5.14f); SimpleTrigger simpleTrigger = new SimpleTrigger("mytrigger",sched.DEFAULT_GROUP, new Date(),null,SimpleTrigger.REPEAT_INDEFINITELY,30L*1000L); sched.scheduleJob(jd, simpleTrigger); sched.start(); } public static void main(String args[]){ try{ new NewSchedule(); }catch(Exception e){} } ; } Download this code Output of the program : log4j:WARN No appenders could be found for logger (org.quartz.simpl.SimpleThreadPool). log4j:WARN Please initialize the log4j system properly. Welcome to RoseIndia.Net :Wed Feb 21 14:43:55 GMT+05:30 2007 job Name :myjob Job Group Name :group Name :Rose India Float value is :5.14 Welcome to RoseIndia.Net :Wed Feb 21 14:44:25 GMT+05:30 2007 job Name :myjob Job Group Name :group Name :Rose India Float value is :5.14 Stateful vs. Non-Stateful Jobs A Job instance can be defined as Stateful or Non-Stateful. Non-Stateful jobs only have their JobDataMap and it stored at the time when the jobs are added to the scheduler. This means during execution of the job any changes are made to the job data map will be lost and a Stateful job is just opposite - its JobDataMap is re-stored after every execution of the job. One disadvantage of Stateful job is that it cannot be executed concurrently. Or in other words: In Stateful job, and a trigger attempts to 'fire' the job while it is already executing, the trigger will block (wait) until the previous execution completes. You can specify a job as Stateful by implementing the StatefulJob interface instead of Job interface. Other Attributes of Jobs Some other properties which can be defined for a job instance through the JobDetail object: Durability - if a job is non-durable, it is automatically deleted from the scheduler once there are no longer any active triggers associated with it. Volatility - if a job is volatile, it is not persisted between re-starts of the Quartz scheduler. RequestRecovery - if a job is "requests recovery", and the job is executing during 'hard shutdown' time 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. JobListeners - a job can have a set of zero or more JobListeners associated with it. When the job executes, the listeners are notified. The Job execute() Method The only JobExecutionException (including RunTimeExceptions) are allowed to throw from the execute() method. That's why, you should wrap the entire body of this method in 'try-catch' block. In this section we will try to provide the brief description of triggers. As we know that the trigger objects are used for executing the jobs or firing jobs. When we wish to schedule the jobs, then we can used the properties of triggers. Here we are going to provide two types of triggers like: Simple Trigger and Cron Trigger for scheduling the jobs. Now, we will see how to use these triggers with the Calendar interface. Calendars Quartz Calendars (not java.util.Calendar) objects can be associated with triggers object at the time of the triggers are stored in the scheduler. It is very useful to excluding blocks of time to fire the triggers schedule. For example, we wish to create a trigger that fires a job every weekday at 4.15 am, in this case we would to add a Calendar that excludes all of the holidays. We are going to implement the Calendar interface: Here is the code of Calendar interface: package org.quartz; public interface Calendar { public boolean isTimeIncluded(long timeStamp); public long getNextIncludedTime(long timeStamp); } isTimeIncluded(long timeStamp); Above method returns a Boolean type values either true or false. Which takes long type value (timeStamp) in milliseconds and included by the Calendar interface. getNextIncluedeTime(long timeStamp); This method takes long type value (timeStamp) in millisecond and determine the next time which is included by the Calendar after the given time. The Quartz consists of the org.quartz.impl.HolidayCalendar class. The Calendar object integrated with the scheduler through the addCalendar() method. If you want to use the HolidayCalendar then first of all you must be initialized it, after that you will use the addExcludedDate(Date date) method in the program. This method is used to exclude the days from the scheduling. Here we are going to implement a simple Calendar object to add and exclude with it: HolidayCalendar hcal cal.addExcludedDate(Date date); = new HolidayCalendar(); sched.addCalendar("myHoliday", hcal, false); Misfire Instructions The triggers contains another important property that is "misfire instruction". Sometimes, a persistent trigger misses to its firing time because the scheduler being shutdown, in this case the misfire is occurred. More descriptions about the misfire instruction will provide below with the Simple and Cron Triggers. The misfire instruction can be used with trigger object by using the setMisfireInstruction() method. Simple trigger can be used to one shot execution or fire a job and in other words, we can say that just a single execution of job to the specified time. Sometimes, we wish to execute or fire a job at the specified time that has till 'N' repetition times with delay between 'T' executions of jobs that means if you need to have a job execute exactly once at a specific time, or at a specific moment in time that is followed by repeats at a specified time interval. The SimpleTrigger properties contains: a start-time and end-time, a repeat count and a repeat interval. The repeat count properties can be zero, a positive integer or the constant value SimpleTrigger.REPEAT_INDEFINITELY. The repeat interval property must be zero or a positive long value that represents a number of milliseconds. The end-time property over-rides the repeat count property that can be used for creating a trigger which fires every some specified time (20 seconds). It has to compute the number of times that would be repeated between the start-time and end-time, if we want to specify the end-time then we can use a repeat count of REPEAT_INDEFINITELY. We are going to implement a SimpleTrigger constructor to following types: public SimpleTrigger(String name, String group, Date startTime, Date endTime, int repeatCount, long repeatInterval); This is the constructor of java.org.quartz.SimpleTrigger class. Which fires or executes at the specified time and repeat at the specified number of times. It takes following arguments like this: name: This is the name of Simple Trigger. group: This is the name of scheduler group. startTime: This is the time for firing or executing the Trigger. endTime: This is the time for the Trigger to drop repeat firing or executing. repeatCount: This is the number of repeat firing or executing the Trigger by using the REPEAT_INDEFINITELY for no any foundation of time. repeatInterval: This is the time (milliseconds) for stopping or pausing the repeat firing. There are following examples for implementing the SimpleTrigger: 1. Example SimpleTrigger : Create a simple trigger which fires exactly once, 20 seconds from now: long startTime = System.currentTimeMillis() + (20L*1000L); SimpleTrigger strigger = new SimpleTrigger("mySimpleTrigger", sched.DEFAULT_GROUP, new Date(startTime), null, 0, 0L); 2. Example SimpleTrigger : Create a simple trigger that fires quickly and repeats every 20 seconds: SimpleTrigger strigger = new SimpleTrigger("mySimpleTrigger", sched.DEFAULT_GROUP, new Date(), null, SimpleTrigger.REPEAT_INDEFINITELY, 20L * 1000L); 3. Example SimpleTrigger: Create a Simple Trigger that fires quickly and repeats every 10 seconds until 50 seconds from now: long endTime = System.currentTimeMillis() + (50L * 1000L); SimpleTrigger strigger = new SimpleTrigger("mySimpleTrigger", sched.DEFAULT_GROUP, new Date(), new Date(endTime), SimpleTrigger.REPEAT_INDEFINITELY, 10L * 1000L); 4. Example SimpleTrigger: Create a Simple Trigger that fires on February 19 of the year 2007 at accurately 9:15 am, and repeats 10 times with 20 seconds delay between each firing. java.util.Calendar cal = cal.set(cal.HOUR, cal.set(cal.MINUTE, cal.set(cal.SECOND, cal.set(cal.MILLISECOND, 0); new java.util.GregorianCalendar(2007,cal.FEB, 19); 9); 15); 0); Data startTime = cal.getTime(); SimpleTrigger trigger = new SimpleTrigger("mySimpleTrigger", sched.DEFAULT_GROUP, startTime, 10, 20L*1000L); Simple Trigger Misfire Instructions When the misfire instruction occurs then what should to do the Quartz. There are following misfire instructions to use for informing the Quartz. MISFIRE_INSTRUCTION_FIRE_NOW MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_EXISTING_COUNT Here is the also mis-fire instruction that have used for all trigger types: Trigger.MISFIRE_INSTRUCTION_SMART_POLICY When we will use the "smart policy" misfire instruction with the triggers then the SimpleTrigger determine between its various types of mis-fire instructions that depends upon the configuration and state of the specified SimpleTrigger instance. The JavaDOC has the SimpleTrigger.updateAfterMisfire() method that describes the brief description of this dynamic behavior. The CronTriggers are more useful than the SimpleTrigger, if we want to performed the job triggering based on the calendar schedules such as "every day", "every weekday" etc. This is also useful when we need to fire jobs in a schedule that is based on the calendar schedule on the exact specified time intervals of SimpleTrigger. Here we will execute an expression that fires at 8:30, 9:30, 10:30, and 11:30 on every Monday and Saturday. Cron Expression The Cron-Expressions are strings which are used for configuring the instances of CronTrigger. The Cron-Expressions made up of following sub-expressions that performs individual works according to it's schedule and that is separated by the white-space. : 1. 2. 3. 4. 5. 6. Seconds Minutes Hours Day-of-Month Month Day-of-Week Example: The Cron-expression string is "0 0 10 ? * SUN" that means "every Sunday at 10 am". This example reads only the "SUN" from weekday and replaces to all weekday. The Wild-cards ('* ' character) that can be used for inserting the every possible value of this field. The '*' character is used in the "Month" field that means "every month" and "Day-Of-Week" field means "every day of the week". All fields have some specific values that are specified by us. Such as the numbers 0 to 23 for hours, 0 to 59 that is used for minutes and seconds, 0 to 31 for Day-of-Month but here, we should more careful about how many day are used in a month. Months have the specified values between 0 to 11, for this we will use the some string as like: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC. Similarly, the Days-of-week has specified values between 1 to 7. Here 1 for Sunday, 2 for Monday, 3 for Tuesday,....... so on. But here we will use the some string for this like: SUN, MON, TUE, WED, THU, FRI and SAT. The Cron-Expressions are used the '/' character for increments to specify values. If we will put "0/10" in the seconds field that means every 10 seconds will start at second zero and if we will put "0/20" in the Minutes field then it will simply fires every 20 minutes. The Cron-Expressions are also used the '?' character that allows for the day-of-month and dayof-week fields for specifying "no specific value". The 'L' character, it is the short form of "last" that allows us for using the day-of -month and dayof-week fields. If we can use the value 'L' in the day-of-month field that means last day of the month like: 31 for January, 28 for February not the leap years. In case of day-of-week field that means "7 stands for SAT". There are following example of expressions for specify the JavaDOC for CronTrigger: 1.Example: Write an expression to create a trigger that fires ever 10 minutes. "0 0/10 * * * ?" 2.Example: Write an expression to create a trigger that fires every 10 minutes, at 10 seconds after the minute. "10 0/10 * * * ?" (That means each firing after the 10 seconds interval like: 8:00:00am, 8:10:00am,8:20:00am etc.) 3.Example: Write an expression to create a trigger that fires at 9:30, 10:30, 11:30, 12:30 and 13:30 on every Sunday and Saturday. "0 30 9-13 ? * SUN, SAT" In this section we are just providing you a Cron Trigger example and by this, you can better understand the working of cron trigger. Description of Program In this following program we are just creating a Quartz application with the help of cron trigger. For this, we have to make two classes one is job class (CronJob.java) that is implementation of Job interface and second one is scheduler class. In scheduler class (CronSchedule.java), firstly we need the object of scheduler and for this we have to instantiate the SchedulerFactory and invoke its getScheduler() method. After getting the scheduler we require to instantiate the JobDetail object with the attributes like job name, group name and the job class name. Now we used Cron Trigger to firing the job repetitively after the specified time. After doing all these things we get the simple message like "Welcome to RoseIndia.net" and date specified intervals. Description of Code CronTrigger("cronTrigger","group2","0 0/1 * * * ?"); By the above constructor we just create the CronTrigger object with the trigger name, group name and the cron expression. Cron Expression are used to configuring the instance of CronTrigger. And this corn expression are used to just execute the job after one minute indefinitely. Here is the code of Job Class (CronJob.class) : import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.*; public class CronJob implements Job { public void execute(JobExecutionContext arg0) throws JobExecutionException { System.out.println("Welcome to RoseIndia.net :"+new Date()); } } Download this code Here is the code of Schedule Class (CronSchedule.class) : import org.quartz.CronTrigger; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; import org.quartz.JobDetail; public class CronSchedule { public CronSchedule ()throws Exception { SchedulerFactory sf=new StdSchedulerFactory(); Scheduler sched=sf.getScheduler(); JobDetail jd=new JobDetail("job1","group1",CronJob.class); CronTrigger ct=new CronTrigger("cronTrigger","group2","0 0/1 * * * ?"); sched.scheduleJob(jd,ct); sched.start(); } public static void main(String args[]){ try{ new CronSchedule(); }catch(Exception e){} } } Download this code Output of the program : log4j:WARN No appenders could be found for logger (org.quartz.simpl.SimpleThreadPool). log4j:WARN Please initialize the log4j system properly. Welcome to RoseIndia.net GMT+05:30 2007 :Fri Feb 23 15:17:00 Welcome to RoseIndia.net GMT+05:30 2007 :Fri Feb 23 15:18:00 Welcome to RoseIndia.net GMT+05:30 2007 :Fri Feb 23 15:19:00 In this quartz tutorial, we will learn how to implement more than one triggers and jobs with a quartz scheduler. We know that the scheduler is a main interface of quartz scheduler that contains Job Details and Triggers. It has a name and group associated with them and it can be identified by the single scheduler. See below for an implementation of more than one job details and triggers. Description of program: Here, we are going to implement more than one job details and triggers with the help of following program that jobs name, triggers name and its firing date and time. We have known about the quartz application, when we wish to implement any quartz application then we will require two classes: one is Scheduler class (MoreTriggerSchedule.java) and another is Job class (MoreTriggerJob.java) that will implement in the Job interface. In this program, the Job class will represent the job's name, trigger's name and its firing times with day and date as well as GMT format time. The scheduler class is a main class of this quartz application that implements the Scheduler instance. But, when we will go to implement this instance then we need an object of the SchedulerFactory invoked by the getScheduler() method. Now, we start the scheduler by using the start() method. The quartz Scheduler has JobDetail and CronTrigger objects. These are attached in the quartz by the scheduleJob() method. After completing the entire processes then we will get all jobs and triggers to be used in it. Description of code: (0/5 * * * * ?): This corn expression provides the facility for firing the trigger every 5 seconds to un-limit time. (0/8 * * * * ?): This expression allows to the trigger for firing every 8 seconds to infinite time. Here is the code of Scheduler class (MoreTriggerSchedule.java): import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; public class MoreTriggerSchedule { public static void main(String args[]){ try{ new MoreTriggerSchedule(); }catch(Exception e){ e.printStackTrace(); } } public MoreTriggerSchedule()throws Exception{ JobDetail jDetail; CronTrigger cronTrigger; SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sche = sf.getScheduler(); sche.start(); jDetail = new JobDetail("Job1","group1",MoreTriggerJob.class); cronTrigger = new CronTrigger("cronTrigger1","group1","0/5 * * * * ?"); sche.scheduleJob(jDetail, cronTrigger); jDetail = new JobDetail("Job2","group2",MoreTriggerJob.class); cronTrigger = new CronTrigger("cronTrigger2","group2","0/8 * * * * ?"); sche.scheduleJob(jDetail, cronTrigger); } } Download this example. Here is the code of Job class (MoreTriggerJob.java): import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class MoreTriggerJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { // TODO Auto-generated method stub System.out.println("Job name: " + context.getJobDetail().getName()); System.out.println("Trigger name: " + context.getTrigger().getName()); System.out.println("Firing Time: " + context.getFireTime()); } } Download this example. Output of program: log4j:WARN No appenders could be found for logger (org.quartz.simpl.SimpleThreadPool). log4j:WARN properly. Please initialize the log4j system Job name: Job2 Trigger name: cronTrigger2 Firing Time: Tue Feb 27 10:06:56 GMT+05:30 2007 Job name: Job1 Trigger name: cronTrigger1 Firing Time: Tue Feb 27 10:07:00 GMT+05:30 2007 Job name: Job2 Trigger name: cronTrigger2 Firing Time: Tue Feb 27 10:07:00 GMT+05:30 2007 In this section we will see how to shutdown the scheduler in quartz application. As we know that the scheduler is a main interface of a Quartz Scheduler it maintains the list of JobDetail and Trigger. If we will implement the Scheduler instances then must have to need the SchedulerFactory object. After implementation of this, we have to start it by using the start() method then it will execute (fire) any jobs. Shutdown process of scheduler is given below with code: Description of program: In this program we are going to shutdown the scheduler (SDS) of a quartz application and get the job name, group name, trigger name and its firing time. When we discuss about implementation of any quartz application then we needed two classes: one is scheduler class (StatusSchedule.java) and another is job class (StatusJob.java) that implements the Job interface. For this, firstly we need to implement a quartz scheduler. If we will implement the quartz scheduler then it requires the instance of SchedulerFactory and invokes its getScheduler() method. After that we will start this scheduler with the help of start() method. We know that the quartz scheduler have list of JobDetail and Trigger. The JobDetail object has the job name, job group and its class name that implements in Job interface and the CronTrigger object has the trigger name, trigger group and string type cron expression that is used for configuring the instance of CronTrigger for firing the job relatively after some specified time. Both are added in the quartz scheduler by using the scheduleJob() method. After doing this, it will check the scheduler is shutdown or not. If the scheduler is shutdown then it displays the message "Scheduler is shutdown" and "Job can't be executed here." Otherwise it will show "Scheduler isn't shutdown" and "Job is executed here.". Now, we will get the result like: job name, group name, trigger name and it firing times. Description of code: shutdown(); Above method used to shutdown the quartz scheduler that means breaking the scheduler firing of triggers. The scheduler can not be re-started here. isShutdown(); This method has the scheduler reports and returns a Boolean type date either true or false. When it will return true that means scheduler is shutdown otherwise it couldn't be shutdown and operations are executed here. getFireTime(); This method has exact trigger firing time. Here is the code of Scheduler class (StatusSchedule.java): import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; public class StatusSchedule { public static void main(String args[]) { try{ new StatusSchedule(); }catch(Exception e){ e.printStackTrace(); } } public StatusSchedule()throws Exception{ SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sche = sf.getScheduler(); sche.start(); JobDetail jDetail = new JobDetail( "Show status",sche.DEFAULT_GROUP,StatusJob.class); CronTrigger cronTrigger = new CronTrigger( "Cron Trigger",sche.DEFAULT_GROUP,"0 0/1 * * * ?"); sche.scheduleJob(jDetail, cronTrigger); // sche.shutdown(); if(sche.isShutdown()){ System.out.println("Scheduler is shutdown!"); System.out.println("Job cann't be executed here."); } else{ System.out.println("Scheduler isn't shutdown!"); System.out.println("Job is executed here."); } } } Download this code. Here is the code of Job class (StatusJob.java): import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class StatusJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { // TODO Auto-generated method stub String name = context.getJobDetail().getName(); String group = context.getJobDetail().getGroup(); System.out.println("Job name: " + name + "\t" + "Group: " + group); System.out.println("Trigger name: " + context.getTrigger().getName()); System.out.println("Friring Time: " + context.getFireTime()); } } Download this code. Output of program (Scheduler is not shutdown) : log4j:WARN No appenders could be found for logger (org.quartz.simpl.SimpleThreadPool). log4j:WARN properly. Please initialize the log4j system Scheduler isn't shutdown! Job is executed here. Job name: Show status Group: DEFAULT Trigger name: Cron Trigger Friring Time: Mon Feb 26 09:47:00 GMT+05:30 2007 Output of program (Scheduler is shutdown) : log4j:WARN No appenders could be found for logger (org.quartz.simpl.SimpleThreadPool). log4j:WARN properly. Please initialize Scheduler is shutdown! Job cann't be executed here. the log4j system To perform any action you create Listeners objects and these actions are based on events occurring within the scheduler. TriggerListeners receive events related to triggers, and JobListeners receive events related to jobs. Events related to Trigger : trigger firings, trigger mis-firings and trigger completions. Events related to Job : a notification when the job is about to be executed, and a notification that the job has completed execution For creating listener, simply create an object that implements either the org.quartz.JobListener and/or org.quartz.TriggerListener interface. Then during run time listeners are registered with the scheduler, and must be given a name. Listeners can be registered as either "global" or "nonglobal". Global listeners receive events for ALL triggers/jobs, and non-global listeners receive events only for the specific triggers/jobs that explicitly name the listener in their getJobListenerNames() or getTriggerListenerNames() properties. scheduler.addGlobalJobListener(myJobListener); or scheduler.addJobListener(myJobListener); SchedulerListeners SchedulerListeners are much same as TriggerListeners and JobListeners, but they receive notification of events within the Scheduler itself - its not necessary events related to a specific trigger or job. Events related to Scheduler : the addition of a job/trigger, the removal of a job/trigger, a serious error within the scheduler, notification of the scheduler being shutdown, and others. SchedulerListeners are created and registered in the same way as the Trigger & Job Listener, but there is no distinction between global and non-global listeners. SchedulerListeners can be virtually any object that implements the org.quartz.SchedulerListener interface Job Store are used to keep track of all the "work data" that you give to the scheduler: jobs, triggers, calendars, etc. The important step for Quartz Scheduler step is selecting the appropriate JobStore. You declare which JobStore your scheduler should use (and it's configuration settings) in the properties file (or object) that you provide to the SchedulerFactory that you use to produce your scheduler instance. The JobStore is for behind-the-scenes use of Quartz itself. That's why never use JobStore instance directly in you code. Through configuration you have to tell to Quartz which JobStore to use, but then you should only work with the Scheduler interface in your code. RAMJobStore RAMJobStore is used to keep all of its data in RAM. That's why it is most performant (lightning fast in terms of CPU time) and simple to configure. The disadvantage is only that at the time of application crashes or ends all of the scheduling information is lost - this means RAMJobStore cannot honor the setting of "non-volatility" on jobs and triggers. In other words, this method's major deficiency is lack of data persistence because it kept all the data in RAM, all information will be lost upon an application or system crash. Configuring Quartz to use RAMJobStore : org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore To solve this problem, Quartz offers the JDBCJobStore. This job store keeps all data in a database through JDBC. The trade-off for data persistence is a lower level of performance, as well as a higher level of complexity. JDBCJobStore As the name infers, it stores all data in a database via JDBC. That's why it is a little bit more complicated to configure and it is also as faster than RAMJobStore. But the performance deficiency is not terrible bad, especially when you make the database with indexes on the primary keys. The JDBCJobStore is compatible with all major databases, it mostly used with Oracle, MySQL, DB2, MS SQLServer2000. For using JDBCJobStore your application required two steps. These are follows : 1. You must create the database tables to be used by the job store. Quartz offers a series of table-creation SQL scripts that ease the setup process. In Quartz distribution ""docs/dbTables" directory you find these SQL scripts. If there is no script according to you database type, the modify the existing ones in any way according to your database. In these scripts all table names start with "QRTZ_" prefix. This prefix can be anything according to your wish, but you have to inform JDBCJobStore what the prefix is (in your Quartz properties). Different prefixes can be useful for creating multiple set of tables, for multiple scheduler instances, within the same database. org.quartz.jobStore.tablePrefix = QRTZ_(optional, customizable) 2. You must set the JobStore class properties of your Quartz configuration : After creating table, you have to decide: what type of transactions you application needs, before configuring & firing up the JDBCJobStore. If your application don't need to tie the scheduling commands like adding trigger and removing trigger, to other transaction, then Quartz manage the transaction by using JobStoreTX as your JobStore. org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX If you need Quartz to work along with other transactions (i.e. within a J2EE application server), then you should use JobStoreCMT - in that case Quartz will let the application server container manage the transactions. org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT Then select the DriverDelegate for JobStore to use. It is responsible for doing any JDBC work. StdJDBCDelegate is a delegate that uses JDBC code, SQL statements to do its work. Some other delegates are include in the package "org.quartz.impl.jdbcjobstore" and in its sub-packages like DB2v6Delegate, HSQLDelegate, MSSQLDelegate, PostgreSQLDelegate, WeblogicDelegate and OracleDelegate. After selecting the delegate set its class name for JDBCJobStore to use: org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate JDBCJobStore can get the connections to your database by setting up a DataSource. In Quartz properties DataSource can be defined in different approaches. One approach is, Quartz can create and manage the DataSource itself through providing the all connection information to the database. And another approach is, DataSource is used by Quartz which is managed by an application server that Quartz is running inside of - by providing JDBCJobStore the JNDI name of the DataSource. org.quartz.jobStore.dataSource = qzDS If your Scheduler is very busy that means it always executing the same number of jobs as the size of the thread pool, then in the DataSource you should set the number of connections about the size of the thread pool + 1. Quartz is architected in modularized way, that's why before running it, several components need to be snapped together. Components need to be configure before Quartz : ThreadPool - It provides a set of Threads to Quartz and Quartz used these Threads for executing jobs. The more threads available in pool allows the number of jobs can run concurrently. Some Quartz users find that they need 5 threads are enough because they have fewer jobs at any given time, but generally all jobs are not scheduled to execute at the same time and the jobs complete quickly. But some other users find that they need 10,50 and 100 threads because they have some thousands of triggers with various schedule, which can end up by around 10 and 100 jobs trying to execute at any given time. There is no any rule for finding the size of scheduler's pool, it is only depend on what you are using for the scheduler. Too many threads can bog down your system but make sure you have enough threads for executing the jobs. A ThreadPool interface is defined in the "org.quartz.spi" package. Quartz ships with a simple thread pool named org.quartz.simpl.SimpleThreadPool. It simply maintains a fixed set of threads in its pool that never shrinks and never grows. But it is otherwise quite robust and is very well tested - as nearly everyone using Quartz uses this pool. JobStore - Job Store are used to keep track of all the "work data" that you give to the scheduler: jobs, triggers, calendars, etc. The important step for Quartz Scheduler step is selecting the appropriate JobStore. You declare which JobStore your scheduler should use (and it's configuration settings) in the properties file (or object) that you provide to the SchedulerFactory that you use to produce your scheduler instance. DataSource - JDBCJobStore can get the connections to your database by setting up a DataSource. In Quartz properties DataSource can be defined in different approaches. One approach is, Quartz can create and manage the DataSource itself through providing the all connection information to the database. And another approach is, DataSource is used by Quartz which is managed by an application server that Quartz is running inside of - by providing JDBCJobStore the JNDI name of the DataSource. Scheduler - Finally, you need to create the instance of your Scheduler. Now the Scheduler itself needs to be given a name, told its RMI settings, and handed instances of a JobStore and ThreadPool. The RMI settings include the Scheduler should create itself as an RMI server object, what host and port to use, etc.. StdSchedulerFactory can create Scheduler instances that are actually proxies (RMI stubs) to Schedulers created in remote processes. StdSchedulerFactory StdSchedulerFactory is a class, that is implementation of org.quartz.SchedulerFactory interface and does all of its work of creating a Quartz Scheduler instance based on the content of properties file. Generally, properties are stored in and loaded from a file, but can also be created by your program and handed directly to the factory. Simply invoking getScheduler() on the factory you get the instance of scheduler. DirectSchedulerFactory DirectSchedulerFactory is a class, that is singleton implementation of SchedulerFactory interface. It is useful for those user that want to create their Scheduler instance in more programatic way. Generally, it can be used for the following reasons: (1) it requires the user to have a greater understanding of what they're doing (2) it does not allow for declarative configuration - or in other words, you end up hard-coding all of the scheduler's settings. Logging "org.apache.commons.logging" framework is used by Quartz for its logging needs. Quartz does not produce so much logging information, it produce some information during initialization and then messages about serious problems while jobs are executing. Clustering Clustering feature works with only JDBCJobStore. It include job fail-over and loaded - balancing. Setting up the "org.quartz.jobStore.isClustered" property to "true" for enabling the clustering. Each instance in the cluster used the same copy of the quartz.properties file. Exceptions of this would be to use properties files that are identical, with the following allowable exceptions: Different value for the "org.quartz.scheduler.instanceId" property and different thread pool size. Every node in the cluster MUST have a unique instanceId, which is easily done by placing "AUTO" as the value of this property. Never use cluster features on separate machines, until and unless their clocks are synchronized using some form of time-sync service that runs very regularly. And you can get a serious data corruption if you fire-up a non clustered instance against the same set of tables that any other instance is running against. JTA Transaction Quartz jobs can execute within a JTA transaction (UserTransaction) by setting the "org.quartz.scheduler.wrapJobExecutionInUserTransaction" property to "true". After this setting, a JTA transaction will begin() just before the Job's execute method is invoked, and commit() just after the call to execute terminates. Miscellaneous Features Plug-Ins For plugging-in additional functionality Quartz provides an org.quartz.spi.SchedulerPlugin interface. Plugins are ship with Quartz to provide various utility capabilities. And that can be found documented in org.quartz.plugins package. Jobs Quartz also includes a number of utility jobs. You can add this additional utility jobs in you application for doing something like sending email and invoking EJBs. These out-of-the-box Jobs can be found documented in the org.quartz.jobs package. In this section, we will learn coding methods of establishing the connection between MySQL database and quartz application for updating and manipulating the data of MySQL database tables. Here, we are going to provide a simple way for establishing the connection with the help of following program. See below for detail information. Description of program: As we know a quartz application needs two classes: first is scheduler class (ConnectionScheduler.java) and second one is job class (ConnectionJob.java) that implement in the Job interface. We will require the SchedulerFactory instance for implementing the scheduler. After that we have to start the scheduler by using the start() method then we will add jobs with the help of JobDetail() method. Which contains the name of jobs, job groups and the class name that has some job to be performed by users. Then we have to create a trigger to perform the job at a specified time. Both (JobDetail and CronTrigger) objects are added in the quartz scheduler to the scheduleJob() method. By following the entire process we will implement the scheduler class. Now, we will require the job class that establishes the connection with the MySQL database by using the JDBC driver in forName() method. If the JDBC driver doesn't support then exception is occurred that can be thrown by the ClassNotFoundException and it will display a message "Class not found!". After getting the JDBC driver we can give the string type 'url' in the getConnection() method. If we get the connection then it displays the connection with a message "Connection is created!" and also shows the job name, group name, trigger name and its firing time. Here is the code of Scheduler Class (ConnectionScheduler.java): import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; public class ConnectionScheduler { public static void main(String arg[]){ try{ new ConnectionScheduler(); } catch(Exception e){ e.printStackTrace(); } } public ConnectionScheduler()throws Exception{ SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sche = sf.getScheduler(); sche.start(); JobDetail jDetail = new JobDetail( "JDBC Connection","mysql",ConnectionJob.class); CronTrigger crTrigger = new CronTrigger( "cronTrigger","mysql","0/8 * * * * ?"); sche.scheduleJob(jDetail, crTrigger); } } Download this code. Here is the code of Job Class (ConnectionJob.java): import java.sql.DriverManager; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import com.mysql.jdbc.Connection; import com.mysql.jdbc.Statement; public class ConnectionJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { // TODO Auto-generated method stub System.out.println("Job name:"+context.getJobDetail().getName()); System.out.println("Group name:"+context.getJobDetail().getGroup()); System.out.println("Trigger name:"+context.getTrigger().getName()); System.out.println("Firing Time:"+context.getFireTime()); try{ Class.forName("com.mysql.jdbc.Driver"); } catch(ClassNotFoundException c){ System.out.println("Class not found!"); } try{ String url = "jdbc:mysql://localhost/JDBCquartz?user=root&password=root"; Connection con = (Connection)DriverManager.getConnection(url); System.out.println("Connection :"+con); System.out.println("Connection is created!"); con.close(); System.exit(0); } catch(Exception s){ System.out.println("Doesn't establish the connection!"); System.exit(0); } } } Download this code.