Managing the Scheduler
Besides starting the Scheduler, you might need to perform a few other operations on the Scheduler during your application's lifetime. These Scheduler operations include querying,
Starting the Scheduler
Starting the Scheduler couldn't be more straightforward. When you have an instance of a Scheduler that has been properly
//Create an instance of the Scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //Start the scheduler scheduler.start();
When the start() method is called, the Scheduler starts looking for jobs that need to be executed. You use the start() method when you have a new Scheduler instance and when the Scheduler has been placed into standby mode. After shutdown() has been called, you can no longer call start() on that Scheduler instance.
Putting the Scheduler in standby mode causes the Scheduler to temporarily stop looking for jobs to execute. As an example, suppose your Scheduler gets its job information from a database and you need to restart the database. You could either restart Scheduler when the database is back online or just put the Scheduler in standby mode. You can put a Scheduler in standby mode by calling the standby() method:
public void standby() throws SchedulerException;
In standby mode, the Scheduler does not attempt to execute jobs because the thread that searches for jobs that need executing is
Stopping the Scheduler
You can use two versions of the shutdown() method to stop the Scheduler:
public void shutdown(boolean waitForJobsToComplete) throws SchedulerException; public void shutdown() throws SchedulerException;
The only difference between these two
Previous chapters took a cursory look at Quartz jobs, but now we go through a more formal discussion of Quartz jobs and how to use them.
What Is a Quartz Job?
Quite simply, a Quartz job is a Java class that
These examples are just a few; you surely can come up with your own. Anything that you can do in Java can become a job.
The org.quartz.Job Interface
The only requirement that Quartz puts on your Java class is that it must implement the org.quartz.Job interface. Your job class can implement any other interfaces that it wants or extend any class that it needs, but it or a superclass must implement the job interface. The job interface defines a single method:
public void execute(JobExecutionContext context) throws JobExecutionException;
When the Scheduler determines that it is time to run the job, the
method is called, and a
object is passed to the job. The only
When the Scheduler calls a job, a JobExecutionContext is passed to the execute() method. The JobExecutionContext is an object that gives the job access to the runtime environment of Quartz and the details of the job itself. This is analogous to a Java Web application in which a servlet has access to the ServletContext. From the JobExecutionContext, the job can access everything about its environment, including the JobDetail and trigger that were registered with the Scheduler for the job. Listing 4.4 shows a job called PrintInfoJob that prints some information about the job.
As you can see from Listing 4.4, Quartz jobs can be very basic. The
object, which is stored in the
and prints some basic details about the job. The
Listing 4.4. The PrintInfoJob Shows How to Access the JobExecutionContext
You first saw the org.quartz.JobDetail class back in Chapter 3. A JobDetail instance is created for every job that is scheduled with the Scheduler. The JobDetail serves as the definition for a job instance. Notice in Listing 4.5 that the job isn't the object registered with the Scheduler; it's actually the JobDetail instance .
Listing 4.5. A JobDetail Is Registered with the Scheduler, Not the Job
You can see in Listing 4.5 that the
gets added to the Scheduler, not the job. The job class is part of the
but is not
Setting Job State Using the JobDataMap Object
You can define state for a job using
through its superclass,
Listing 4.6. Use the JobDataMap to Pass Configuration Information to Your Job
In Listing 4.6, the information that we want to pass to the
is stored in the
. Because the
interface, we store state there using a key/value pair configuration. The
includes niceties to make it easier to deal with object conversion. Normally with maps, you have to explicitly convert from object to the known type. The
When the Scheduler eventually calls the job, the job can use the JobDetail to access and use the key/value pairs from the JobDataMap. Listing 4.7 shows the PrintJobDataMapJob .
Listing 4.7. The Job Can Access the JobDataMap THRough the JobExecutionContext Object
When you obtain the JobDataMap, you can use its methods as you might any map instance. Normally, you access the data within the JobDataMap using a predefined key of your choice. You can also iterate through the map itself, as Listing 4.7 shows.
For jobs such as
the properties within the
become an informal contract obligation between the client scheduling the job and the job itself. Job
Stateful Versus Stateless Jobs
You learned from the previous section that information can be inserted into the JobDataMap and accessed by your jobs. For every job execution however, a new instance of the JobDataMap is created with the values that have been stored (for example, in a database) for the particular job. Therefore, there's no way to hold that information between job invocationsthat is, unless you use a stateful job.
In the same way that stateful session beans (SFSB) in J2EE keep their state between calls, the Quartz StatefulJob can hold its state between job executions. However, just like SFSBs, Quartz stateful jobs have some downsides when compared with their stateless counterparts.
Using Stateful Jobs
The Quartz framework offers the org.quartz.StatefulJob interface when you need to maintain state between job executions. The StatefulJob interface extends the standard job interface and adds no methods that you have to implement. You simply implement the StatefulJob interface using the same execute() method as the job interface. If you have an existing job class, all you have to do is change the job interface to org.quartz.StatefulJob.
Two key differences exist between a job and StatefulJob as they are used by the framework. First, the JobDataMap is repersisted in the JobStore after each execution. This ensures that changes that you make to the job data are kept for the next execution.
The other important difference between stateless and stateful jobs is that two or more stateful JobDetail instances can't execute concurrently. Say that you have created and registered a stateful JobDetail with the Scheduler. You also have set up two triggers that fire the job: one that fires every minute and another that fires every five minutes. If the two triggers tried to fire the job at the same time, the framework would not allow that to occur. The second trigger would be blocked until the first one completed.
This requirement has to do with the JobDataMap storage. Because the JobDataMap is stored along with the JobDetail that defines the job instance, thread-safety issues must be taken into consideration. Only one thread can run and update the JobDataMap storage at a time. Otherwise, the data would be erroneous because the second trigger could try to execute the job before the first had a chance to update the storage. Even stranger results could occur if the second execution completed before the first, which is possible, depending on what your job does.
Because of these differences, you should use the
carefully. When you need to prevent concurrent executions of a job, the stateful job is your