You are here: Tips & How-To's > Programming Tricks
Java


Java


Quartz Scheduler: Plain and Simple

When programming Java applications, have you ever needed to run a particular job at a certain time? If your application is running on Linux, you could just schedule a job with crontab and be happy, but what if your application runs on multiple operating systems? Automatically configuring a scheduled task can be time consuming and use a lot of code. 

I was programming a struts application that ran under Tomcat on a Windows Server 2008 server. Sure, I could configure the server to hit an action within my web application and run a specific task at a specified time, but I found it easier for the application itself to manage it's own tasks.

This is where Quartz steps in. It has a scheduler that takes a job and a cron-formatted string for scheduling jobs that run within your application. In the following examples, we'll write a bit of code that prints some text to the console every few minutes. Please note, this is not an in-depth tutorial to Quartz, but still, it should be sufficient for you to get jobs running in any application.

The Job: First, you have to program the job. The job is internal to your program, so I can't tell you how to build it. I can give you a sample class, though, that will give you the idea.

package com.themaskedcrusader.project.jobs

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class MyJob implements Job {

    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("Job is running!");
    }
}

This is a job. It is just a generic POJO that extends the interface org.quartz.Job. You must include the function execute in the class. This is the method the scheduler will call when the time comes around. In my example, the job will simply print some text to the console. Replace all the code withint the execute() method with what you want your program to do when the schedule fires. Simple, innit?

The Scheduler: The other part of the job is the scheduler. This should be setup when the application starts, or can initialized by an action in your application. For my application, I wrote a QuartzPlugin class for Struts that initalizes my jobs and schedules them with the scheduler.

The code is very simple. First, you must initalize two objects that tell the scheduler what to do.

First is the JobDetailImpl class, which tells the scheduler what to do. To set it up:

    JobDetailImpl job = new JobDetailImpl();
    job.setJobClass((Class<? extends Job>) MyJob.class);
    job.setName("Name for Job"); // a Job Name is required.

You do not need to worry about boxing your job class. I use a generic when creating the job further down the page, but you shouldn't have to box it.

Second, you have to initialize the trigger. The class is CronTriggerImpl, and it is initialized:

    CronTriggerImpl trigger = new CronTriggerImpl();
    trigger.setCronExpression("*/5 * * * * ?");
    trigger.setName("Name for Trigger"); // a Trigger Name is required

When you initialize your trigger, you pass in a cron formatted string that tells the scheduler when to fire off your job. I'll briefly explain the cron format at the bottom of this page.

Finally, when you have a job (JobDetailImpl) object and a trigger (CronTriggerImpl) object, you are ready to schedule your job. The following code sets the job up and schedules it with the scheduler.

package com.themaskedcrusader.project.jobScheduler;

import org.quartz.*;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.triggers.CronTriggerImpl;
import com.themaskedcrusader.project.jobs.MyJob1;
import com.themaskedcrusader.project.jobs.MyJob2;

import java.util.HashMap;

public class QuartzSchedulerStartup {
    // This object holds all the initialized jobs that this application will use
    HashMap<JobDetailImpl, CronTriggerImpl> jobs = new HashMap<JobDetailImpl, CronTriggerImpl>();

    public void startScheduler() {
        // logs the startup of your scheduler
        System.out.println("Initializing Quartz Scheduler");

        try{
            // prepare jobs to schedule
            addJob(MyJob1.class,  "0 0 2 * * ?"); // Every day, 2 am
            addJob(MyJob2.class,  "0 0 1 * * ?"); // Every day, 1 am

            // start the Quartz scheduler
            Scheduler scheduler = new StdSchedulerFactory().getScheduler();
            scheduler.start();

            // Loop through jobs HashMap and Schedule each Job
            for (Map.Entry entry : jobs.entrySet()) {
                JobDetailImpl  job = (JobDetailImpl) entry.getKey();
                CronTriggerImpl trigger = (CronTriggerImpl) entry.getValue();
                scheduler.scheduleJob(job, trigger);
            }

    	} catch(SchedulerException e){
    		System.err.println("Failed to initialize scheduler", e);
    	}
    }

    // since we don't know the class called, just that it extends Job, a generic is useful here.
    private void addJob(Class<? extends Job> jobClass, String cronExpression) {
        try {
            JobDetailImpl job = new JobDetailImpl();
            job.setJobClass(jobClass);
            job.setName("Job for " + jobClass.getName();

            CronTriggerImpl trigger = new CronTriggerImpl();
            trigger.setCronExpression(cronExpression);
            trigger.setName("Trigger for " + jobClass.getName();

            jobs.put(job, trigger);
            
        } catch (ParseException e) {
            log.error("Unable to create schedule for class " + jobClass.getName(), e);
        }
    }
}

You'll notice, I created a small private method that creates the Job and Trigger objects and puts them into a HashMap that we eventually loop through to schedule each job. This is for convenience and to make the code a little easier to read. 

That's it, though. You create a job, a trigger, and schedule it with your scheduler. Then you sit back and watch your application run it's own jobs without needing to use an Operating System specific task scheduler.

Variations: I always add a Keyring class to every application (A specific class just for holding global static final variables needed by the application). If you use one too, you could add a static Schedule object to that keyring. Then, any class/action/whatever could add a job to the schedule without having to know before the application is launched that it needs to run.

The Scheduler creates 10 threads that sit idle until a job is called, so you can really pump the Scheduler full of jobs before you have to create a second one.

A word on the Cron: The Quartz cron string is based on Cron for Unix, which is pretty easy to understand after you've worked with it for a while. You can learn more about Cron at the Wikipedia page for it. After you understand the basics of Cron, then pop over to the Quartz documentation to see how it differs from Cron for Unix. The main difference is that the Quartz cron string has support for seconds. So, make sure you remember that when scheduling your jobs so that they run on the schedule you want.

Final Note: I wrote this little bit of awesomeness for myself because I couldn't really follow the other Quartz tutorials on the web. If you found this, for lack of a better word, tutorial useful, I'm glad I could help. If it was a bit confusing to for you, please consider using the Contact Us link and let us know where it needs a little bit of help. We'll do our best to clear it up and make this a bit less confusing.

Downloads: Here are the files you need to download to implement a simple Scheduler in your application



^ TOP