Wednesday, July 2, 2008

Quartz Specifications

Quartz

Quartz is an open source job-scheduling framework written entirely in Java and designed for use in both J2SE and J2EE applications. It offers great flexibility without sacrificing simplicity. You can create simple or complex schedules for executing any job. It includes features such as database support, clustering, plugins, prebuilt jobs for EJB, JavaMail and others, support for cron-like expressions, and many more.

Job Scheduling Made Easy

Quartz is an open source job-scheduling framework written entirely in Java. Don't let the term job scheduling frighten you. While the Quartz framework is packed with many bells and whistles, in its simplest form, it's almost scary how easy it is to use.

Simply create a Java class that implements the org.quartz.Job interface. The Job interface contains a single method:

public void execute(JobExecutionContext context) 
     throws JobExecutionException;

In your Job class, add some logic to the execute() method. Once you configure the Job class and set up the schedule, Quartz will take care of the rest. When the Scheduler determines it's time to notify your Job, the Quartz framework will call the execute() method on your Job class and allow it to do its thing. You don't have to report anything back to the Scheduler or call anything special. Just perform the tasks within the Job and end. 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.

If you've used other popular open source frameworks, like Apache Struts, you'll be comfortable with the design and components in Quartz. Even though the two open source projects solve completely different problems, there are enough similarities that everyday users of open source software will feel right at home. Quartz can be used within a stand- alone J2SE application, as an RMI server, within a web application, and even within a J2EE Application Server.

Inside the Quartz Architecture

In terms of size, Quartz is comparable to most other open source frameworks. It contains approximately 300 Java Classes and Interfaces organized into roughly 12 packages. This compares to approximately 325 Classes and Interfaces and 11 packages in Apache Struts. Although size is rarely a characteristic used to determine the quality of a framework, the point here is that there's a lot of functionality included within Quartz, and functionality and feature set is, and should be, one factor used to judge the quality of a framework, open source or not.

The Quartz Scheduler

At the heart of the Quartz framework is the Scheduler. The Scheduler is responsible for managing the runtime environment for your Quartz application. It doesn't do all of the work by itself, but depends on some very important components within the framework. Quartz is very much about threads and thread management. To ensure scalability, Quartz is based on a multithreaded architecture. When started, the framework initializes a set of "worker threads" that are used by the Scheduler to execute the scheduled Jobs. This is how Quartz is able to run many Jobs concurrently. Quartz relies on a loosely coupled set of ThreadPool management components to manage the thread environment. We'll mention it several times throughout the article, but just about everything in Quartz is either configurable or customizable. So, for example, if you wanted to "plugin" your own ThreadPool management facilities, guess what, you can!

Jobs, Jobs, and More Jobs

In Quartz lingo, a Job is simply a Java class that performs a task. This task can be anything that you can code in Java. The only requirement is that you implement the org.quartz.Job interface and throw a JobExecutionException in the case of a serious error. You saw in the beginning that the Job interface contains a single method, execute().

Once you implement the Job interface and implement the execute() method, Quartz will invoke your Job when it determines it's time for the Job to run. What you do inside the execute() method is entirely up to you. Here are a few examples of things you might want to do inside a Job:

· Use JavaMail (or another Mail framework like Commons Net) to send emails

· Create a remote interface and invoke a method on an EJB

· Get a Hibernate Session, and query and update data in a relational database

· Use OSWorkflow and invoke a workflow from the Job

· Use FTP and move files around

· Call an Ant build script to kick off scheduled builds

The possibilities are endless and that's what makes the framework so powerful. Quartz provides you with the mechanism to establish a very granular, repeatable schedule, and then you just create Java classes that get called to execute.

Job Management and Storage

Once Jobs have been scheduled, the Scheduler needs to remember and keep track of the Jobs and the times to execute them. It wouldn't be very useful if your Job is called 30 minutes late or even 30 seconds early. In fact, Job execution needs to be very exact and prompt about calling the execute() methods on the scheduled jobs. Job storage and management is done through a concept Quartz refers to as a JobStore.

Jobs and Triggers

Jobs are only half the equation. The designers of Quartz made a design choice to separate the Job from the schedule. A Trigger in Quartz is used to tell the Scheduler when a Job should be triggered (or fired). The framework comes with a handful of Trigger types, but the two most commonly used are the SimpleTrigger and the CronTrigger.

The SimpleTrigger is designed for when you need a simple firing schedule. Typically, if you need to fire a Job at a given time and repeat (n) number of times, possibly waiting (m) seconds between firings, then the SimpleTrigger is for you. If on the other hand, you have a much more complicated scheduling need for your Job, then the CronTrigger will probably be required.

The CronTrigger is based on Calendar-like schedules. When you need a Job executed every day at 10:30 a.m., except on Saturdays and Sundays, then a CronTrigger should be used. As the name implies, the CronTrigger is based on the Unix cron expression. As an example, the following Quartz cron expression would execute a Job at 10:15 a.m. every day, Monday through Friday.

0 15 10 ? * MON-FRI

and this expression

0 15 10 ? * 6L 2002-2005

fires at 10:15 a.m. on the last Friday of every month during the years 2002, 2003, 2004, and 2005.

You can't do this with the SimpleTrigger. Either can be used for your Jobs. Which one just depends on your schedule needs.

Scheduling a Job

So, let's take this discussion into the practical realm by looking at an example Job. Suppose that you manage a department that needs to be notified by email whenever a client stores a file on its FTP site. Our Job will FTP to a remote server and download any files found there. It will then send an email containing the number of files found and downloaded. This Job would be handy to prevent someone from having to manually performing this task during the day, not even considering during the night. We can set this Job up to check every 60 seconds and to run indefinitely, 24 hours a day, seven days a week. This is a perfect use of the Quartz framework!

The first step is to create the Job class that will perform the FTP and Email logic. The following Example shows our Quartz Job class, which implements the org.quartz.Job interface.

Example 2. A Quartz Job that downloads files from a FTP site and sends an Email

public class ScanFTPSiteJob implements Job {
    private static Log logger = LogFactory.getLog(ScanFTPSiteJob.class);
 
    /*
     * Called the scheduler framework at the right time
     */
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
 
        JobDataMap jobDataMap = context.getJobDataMap();
 
        try {
            // Check the ftp site for files
            File[] files = JobUtil.checkForFiles(jobDataMap);
 
            JobUtil.sendEmail(jobDataMap, files);
        } catch (Exception ex) {
            throw new JobExecutionException(ex.getMessage());
        }
    }
}

We intentionally kept the ScanFTPSiteJob very simple. We created a utility class for this example called JobUtil. That's not part of Quartz, but it makes sense to build your own library of utilities that various Jobs can reuse. We could have just as easily put all of that code inside the Job class and the Quartz Scheduler would have been just as happy, but reuse doesn't stop just because we're using Quartz.

The parameters that the JobUtil.checkForFiles() and JobUtil.sendEmail() methods use come from the JobDataMap object, which is a Quartz-created object. An instance of it is created for every Job execution and it is a way to pass configuration parameters to your Job class.

The implementation of the JobUtil is not shown here, but we could very easily use the Commons Net from Jakarta to implement both the FTP and Email functionality.

Programmatic vs. Declarative Scheduling

In Example 3, we scheduled our ScanFTPSiteJob using a programmatic approach. That is, we used Java code to setup the Job and Trigger with the Scheduler. The Quartz framework also supports setting up the Job schedule declaratively in XML files. A declarative approach allows us to more quickly modify which Jobs are being executed and when.

The Quartz framework includes a "plugin" that reads an XML file containing Job and Trigger information upon startup of a Quartz application. All Jobs within the XML are added to the Scheduler, along with their associated Triggers. You still have to write the Job classes, of course, but configuring the Scheduler with those Job becomes very dynamic. Example 4 shows an example XML file that performs the same logic as the code in Example 3, but just in a declarative fashion.

Example 4. Quartz Jobs can also be scheduled using an XML File.

    
        
            ScanFTPSiteJob
            DEFAULT
            
                A job that scans an ftp site for files
            
            ScanFTPSiteJob
 
            
                
                    FTP_HOST
                    \home\cavaness\inbound
                
                
                
 
            
        
 
        
            
                ScanFTPSiteJobTrigger
                DEFAULT
                ScanFTPSiteJob
                DEFAULT
                2005-09-11 6:10:00 PM
                
                -1
                60000
            
        
 
    

You should be able to match the XML elements from Example 4 with the Java code from Example 3--they are conceptually the same. The benefit of using a declarative approach like the one shown in Example 4 is that maintenance becomes extremely simple, as changes only require a change to the XML file and a restart of the Quartz application. No source code changes, no recompilation, and no redeployment of the new build.


Clustering Quartz Applications

Quartz applications can be clustered, both horizontally and vertically, depending on your needs. Clustering offers the same benefits as any other type of clustering:

· Scalability

· High Availability

· Load Balancing

Currently, Quartz supports clustering with help from a relational database and one of the JDBC JobStores. In a future version, this constraint will be lifted and clustering will possible with the RAMJobStore and will not require a database.