Saturday, March 31, 2007

Using Quartz Scheduler in a Java EE Web Application

At times, you may have wanted to perform some action periodically in your web application. Quartz is an enterprise grade scheduler which can be used for such a task. Read here for the complete list of features of Quartz. For using the Quartz scheduler library in a Java EE web application, following needs to be done:
  • Include the quartz jars (quartz-all.jar and others in the lib path). In my case, some of the commons-xxx.jar files were already included in the project due to the dependency of another library (displaytag) on those jar files. So in my quartz setup i had to disinclude them. In the lib/build path, i only included, jta.jar and also everything which was not already there in the project from lib/optional path too (they are not many anyway).
  • We then had to create the tables required by quartz for storing job details and triggers across restart of application. This is an optional feature but an important one (which made us decide to use quartz in the first place over the JDK Timer). We used the docs/dbTables/tables_mysql.sql script to create the tables.
  • Then we copied (example_quartz.properties), modified and saved the quartz.properties file in the project and changed the packaging settings to include the properties file in the WEB-INF/classes path in the IDE. In the properties file, we changed the configuration to have quartz point to the data store we created in step 2.
# Configuring Main Scheduler Properties
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = 1
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false

# Configuring ThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 30
org.quartz.threadPool.threadPriority = 5

# Configuring JobStore
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource = quartzDS
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = false

# Configuring datasource
org.quartz.dataSource.quartzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.quartzDS.URL = jdbc:mysql://localhost:3306/mydb
org.quartz.dataSource.quartzDS.user = me
org.quartz.dataSource.quartzDS.password = secret
org.quartz.dataSource.quartzDS.maxConnections = 31

# Rest of config was retained from example_quartz.properties.
  • We added following lines to our web.xml:
<servlet>
<description>Quartz Initializer Servlet</description>
<servlet-name>QuartzInitializer</servlet-name>
<servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class>
<init-param>
<param-name>shutdown-on-unload</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>start-scheduler-on-load</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>


This sets up the initializer servlet which can initialize the default scheduler and start the scheduler at application bootstrap time.
  • Now in our webservice/jsp/servlet of our web application we do the following:
try {
// A. Get the default scheduler.
Scheduler sched = StdSchedulerFactory.getDefaultScheduler();

// B.Generate a unique name identifier for jobs and
// triggers of your application as required.
// One way is to use hashCode() if its a string param.
String dataToPass = "someParamToPassToJob";
int id = dataToPass.hashCode();

// C. Create/Replace a poll job and add it to the scheduler.
JobDetail job =
new JobDetail("job_"+id, "SomeJobGroup", com.mycompany.MyJob.class);
job.setRequestsRecovery(true);
// Pass data to the poll job.
job.getJobDataMap().put("param", dataToPass);
sched.addJob(job, true);

// D. Create a Trigger with unique name
SimpleTrigger trigger = new SimpleTrigger("trig_"+id, "SomeTriggerGroup");


// E. Check if a trigger is already associated with this job
// This step is optional and depends on your application's requirement.

Trigger[] jobTriggers;
jobTriggers = sched.getTriggersOfJob("job_"+id, "SomeJobGroup");

boolean isTriggerAlreadyAssociated = false;
for (Trigger trig : jobTriggers) {
if (trig.getName().equals("trig_"+id) &&
trig.getGroup().equals("SomeTriggerGroup")) {
// the job already has this trigger associated with it
isTriggerAlreadyAssociated = true;
}
}

// F. Associate this trigger with the job
trigger.setJobName(job.getName());
trigger.setJobGroup(job.getGroup());

// G. Initialize the trigger with duration and resolution to fire
trigger.setStartTime(startTime.getTime());
trigger.setEndTime(endTime.getTime());
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(repeatInterval); //in milliseconds

if (isTriggerAlreadyAssociated) {
// Reschedule the job with the existing trigger.
sched.rescheduleJob("trig_"+id, "SomeTriggerGroup", trigger);
} else {
// Schedule the job with the new trigger.
sched.scheduleJob(trigger);
}
} catch (SchedulerException se) {

}
  • Of course, the last thing is to write the Job class which does the actual work. The following is code from examples of Quartz.
public class PrintPropsJob implements Job {

public PrintPropsJob() {
}

public void execute(JobExecutionContext context)
throws JobExecutionException {

JobDataMap data = context.getJobDetail().getJobDataMap();
System.out.println("someProp = " + data.getString("someProp"));
System.out.println("someObjectProp = " + data.getObject("someObjectProp"));
}
}
  • Some important points to note about jobs and triggers:
  1. Jobs have a name and group associated with them, which should uniquely identify them within a single Scheduler.
  2. A Job can be associated with multiple triggers.
  3. Triggers are the 'mechanism' by which Jobs are scheduled.
  4. Many Triggers can point to the same Job, but a single Trigger can only point to one Job.
  5. JobDataMap holds state information for Job instances. JobDataMap instances are stored once when the Job is added to a scheduler. They are also re-persisted after every execution of StatefulJob instances.
  6. JobDataMap instances can also be stored with a Trigger. This can be useful in the case where you have a Job that is stored in the scheduler for regular/repeated use by multiple Triggers, yet with each independent triggering, you want to supply the Job with different data inputs.
  7. The JobExecutionContext passed to a Job at execution time also contains a convenience JobDataMap that is the result of merging the contents of the trigger's JobDataMap (if any) over the Job's JobDataMap (if any).
  8. We can have different job types:
  • Stateful Jobs - where state passed to job (in JobDataMap) is remembered (like static values) across executions of the job. Also, stateful jobs are not allowed to execute concurrently, which means new triggers that occur before the completion of the execute(xx) method will be delayed.
  • Interruptable job - provide a mechanism for having the Job execution interrupted by implementing a callback method interrupt(), which will be called when scheduler's interrupt method is invoked on the Job.
That's all, in short, about how to integrate Quartz scheduler library in a web application.

9 comments:

Naveen D R said...

Hi,
When Quartz is initialized thru initializer servlet, the factory object needs to be put in servlet context. Can i create scheduler object and put it in servlet context and down the line in app, i can simply get scheduler from the context and use it.

vishalvishnoi said...

Can you give some code, how can we schedule single job with multiple trigger.

Anonymous said...

I am having trouble shutting down my scheduler whe using QuartzInitializerServlet.

I keep getting SchedulerException : Scheduler with name 'xyz' already exists.

Any ideas? Is this because of the web.xml entries for the QuartzInitializer.

cdgnfg said...

In English class, one girl Cheap WoW Gold said never buy WoW Gold give up when discussing. It just reminded me wow goldsomething about myself.WoW Gold Maybe it is wow power leveling something about love.WoW Gold My love, which began on July 3rd of WoW Gold 2005 and finished on August wow leveling 23rd of 2008, taught me many things. wow leveling It is not a pleasant world of warcraft power leveling thing to look back on world of warcraft power leveling that. But I know I world of warcraft power leveling must learn something from it,world of warcraft leveling no matter what it is, world of warcraft leveling happiness or sorrow.Our wow gold love did not go on so smoothly and we went through many things.

vikram said...

Hi ,

Actually i have requirement to create scheduler for Report generation of Log files .Using Web app(jsp/servlet) .A user should be able to create schedule to generate report .So how can i perform this using Quartz .How can i customize this with User input and generate report .

thanks

lee said...

China Highlights
China Highlights
China Tours
China Hotels
China Attractions
Beijing China Travel
Shanghai China Travel
Xi'an China Travel
Guilin China Travel
Yangshuo China Travel

Anonymous said...

Welcome to the 2moons dil, In here you can buy the 2moons gold, Do you know that the 2moon dil in the game is very important, If you had more cheap 2moons gold. I think you can get the tall level, quickly come here to buy 2moons dil.

said...

You know ,I have some maple mesos,and my friend also has some
mesos,do you kouw they have the same meaning,Both of them can be called
maplestory mesos,I just want to
buy flyff penya ,because there are many
cheap mesos


You know ,I have some requiem gold,and my friend also has some
requiem lant,do you kouw they have the same meaning,Both of them can be called requiem money,I just want to
requiem online gold,because there are many
cheap requiem lant

Game player said...

I can getSword of the New World Vischeaply,
Yesterday i boughtSword of the New World Gold for my brother.
i hope him like it. i will give Sword of the New World money to him
as birthday present. i like the cheap snw vis very much.
I usually buy vis and keep it in my store.

i can get silkroad gold cheaply,
Yesterday i bought sro gold for my brother.
i hope him like it. i will give silkroad online gold to him
as birthday present. i like the cheap silkroad gold very much.
I usually buy the silk road gold and keep it in my store.