See http://www.ibm.com/developerworks/cn/java/j-quartz/
Quartz is an open source project that provides a wide range of Job Scheduling sets. In this article, Software Engineer Michael Lipton and IT architect soobaek Jang introduced the quartz API, starting from a general overview of the framework, end with a series of code examples that demonstrate basic features of quart. After reading this article and reading the sample code, you should be able to apply the basic features of quartz to any Java application.
Modern web application frameworks have developed in terms of scope and complexity. Each underlying component of an application must also develop accordingly. Job Scheduling is a general requirement for Java applications in modern systems and is also a consistent requirement for Java developers. Although the current scheduling technology has developed much more than the original database trigger mark and independent scheduler thread, Job Scheduling is still not a small problem. The most suitable solution for this problem is the quartz API of opensymphony.
Quartz is an open-source job scheduling framework that provides a simple but powerful mechanism for Job Scheduling in Java applications. Quartz allows developers to schedule jobs by time interval (or day. It realizes the many-to-many relationship between jobs and triggers, and can associate multiple jobs with different triggers. Applications integrated with quartz can reuse jobs from different events and combine multiple jobs for one event. You can configure quartz through the attribute file (in the attribute file, you can specify the data source, global job, and/or trigger listener, plug-in, thread pool, and more for JDBC transactions, however, it is not integrated with the context or reference of the application server at all. The result is that the job cannot access the internal functions of the Web server. For example, when using the WebSphere Application Server, Jobs scheduled by quartz cannot affect the dynamic cache and data sources of the server.
This article uses a series of code examples to introduce the quartz API and demonstrate its mechanism, such as job, trigger, job repository, and attributes.
Getting started
To start using quartz, you must use the quartz API to configure the project. The procedure is as follows:
- Download the quartz API.
- Decompress and place the quartz-x.x.x.jar in the project folder, or place the file in the project's class path.
- Place the jar files in the core and/or optional folders in the project folder or the class path of the project.
- If you use
JDBCJobStore
Put all the JDBC jar files in the project folder or the project class path.
To help readers, I have compiled all necessary files, including DB2 JDBC files, into a zip file. See the download section to download the code.
Now let's take a look at the main components of the quartz API.
Jobs and triggers
The two basic units of the quartz scheduling package are jobs and triggers.JobIs an executable task that can be scheduled,TriggerProvides job scheduling. Although these two entities are easily combined, there is a reason to separate them in quartz, and it is also helpful.
By separating the job to be executed from its scheduling, quartz allows you to modify the scheduling trigger without losing the context of the job itself or the job. In addition, any single job can have multiple triggers associated with it.
Example 1: Job
Implementationorg.quartz.job
Interface to make the Java class executable. Listing 1 provides an example of a quartz job. This class uses a very simple output statement to overwriteexecute(JobExecutionContext context)
Method. This method can contain any code we want to execute (all code examples are based on quartz 1.5.2, which is the stable release version when writing this article ).
Listing 1. simplequartzjob. Java
package com.ibm.developerworks.quartz; import java.util.Date; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class SimpleQuartzJob implements Job { public SimpleQuartzJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("In SimpleQuartzJob - executing its JOB at " + new Date() + " by " + context.getTrigger().getName()); } } |
Note that the execute method accepts oneJobExecutionContext
Object as a parameter. This object provides the runtime context of the job instance. In particular, it provides access to the scheduler and trigger, the two work together to start the job and the jobJobDetail
Object execution. Quartz places the job status inJobDetail
Object andJobDetail
The constructor starts a job instance and separates the execution of the job from the status around the job.JobDetail
Object Storage job listeners, groups, data ing, descriptions, and other job attributes.
Example 2: simple trigger
A trigger can schedule a task. Quartz provides several different triggers with different levels of complexity. In Listing 2SimpleTrigger
Demonstrate the trigger basics:
Listing 2. simpletriggerrunner. Java
public void task() throws SchedulerException { // Initiate a Schedule Factory SchedulerFactory schedulerFactory = new StdSchedulerFactory(); // Retrieve a scheduler from schedule factory Scheduler scheduler = schedulerFactory.getScheduler(); // current time long ctime = System.currentTimeMillis(); // Initiate JobDetail with job name, job group, and executable job class JobDetail jobDetail = new JobDetail("jobDetail-s1", "jobDetailGroup-s1", SimpleQuartzJob.class); // Initiate SimpleTrigger with its name and group name SimpleTrigger simpleTrigger = new SimpleTrigger("simpleTrigger", "triggerGroup-s1"); // set its start up time simpleTrigger.setStartTime(new Date(ctime)); // set the interval, how often the job should run (10 seconds here) simpleTrigger.setRepeatInterval(10000); // set the number of execution of this job, set to 10 times. // It will run 10 time and exhaust. simpleTrigger.setRepeatCount(100); // set the ending time of this job. // We set it for 60 seconds from its startup time here // Even if we set its repeat count to 10, // this will stop its process after 6 repeats as it gets it endtime by then. //simpleTrigger.setEndTime(new Date(ctime + 60000L)); // set priority of trigger. If not set, the default is 5 //simpleTrigger.setPriority(10); // schedule a job with JobDetail and Trigger scheduler.scheduleJob(jobDetail, simpleTrigger); // start the scheduler scheduler.start(); } |
Listing 2 instantiatesSchedulerFactory
To obtain the scheduler. As discussed earlierJobDetail
Object, its constructor must acceptJob
As a parameter. As the name suggests,SimpleTrigger
The instance is quite primitive. After creating an object, set several basic attributes to schedule the task immediately and repeat the task every 10 seconds until the job is executed for 100 times.
There are many other ways to manipulateSimpleTrigger
. In addition to specifying the number of repetitions and repetition interval, you can also specify the maximum execution time or priority of a job in a specific calendar time (discussed later ). The maximum execution time can overwrite the specified number of repetitions to ensure that the running of the job does not exceed the maximum time.
Example 3: cron trigger
CronTrigger
Support RatioSimpleTrigger
More specific scheduling is not very complex. Based on cron expressions,CronTrigger
Supports repetition intervals similar to calendars, rather than a single interval.SimpleTrigger
It is a major improvement.
Cron expressions include the following seven fields:
- Seconds
- Minute
- Hours
- Date in month
- Month
- Week date
- Year (optional)
Special characters
The cron trigger uses a series of special characters, as shown below:
- The backslash (/) character indicates the increment value. For example, in the second field, "5/15" indicates that the value starts from 5th seconds, every 15 seconds.
- Question mark (?) The characters and letters are only available in the Date Field of the month and the date field of the week. The question mark indicates that this field does not contain specific values. Therefore, if you specify a date in the month, you can insert "?" In the Date Field of the week, Indicates that the date value of the week is irrelevant. The letter L isLast. In the Date Field of the month, the task is executed on the last day of the month. In the week Date Field, if "L" exists separately, it is equal to "7". Otherwise, it represents the last instance of the week date in the current month. Therefore, "0l" indicates that it will be executed on the last Sunday of the month.
- The letter (w) in the date field within the month is executed on the workday closest to the specified value. Place "1 W" in the Date Field of the month, which means that the execution is arranged within the first business day of the month.
- The pound sign (#) specifies a specific workday instance for a given month. Put "Mon #2" in the Date Field of the week, indicating that the task is scheduled for the second Monday of the month.
- The asterisk (*) character is a wildcard character, indicating that this field can accept any possible value.
All these definitions may seem a little scary, but after a few minutes of practice, the cron expression will look very simple.
Listing 3 showsCronTrigger
. Note:SchedulerFactory
,Scheduler
AndJobDetail
AndSimpleTrigger
The instantiation In the example is the same. In this example, the trigger is modified. The cron expression specified here ("0/5 ****?") Schedule task execution every 5 seconds.
Listing 3. crontriggerrunner. Java
public void task() throws SchedulerException { // Initiate a Schedule Factory SchedulerFactory schedulerFactory = new StdSchedulerFactory(); // Retrieve a scheduler from schedule factory Scheduler scheduler = schedulerFactory.getScheduler(); // current time long ctime = System.currentTimeMillis(); // Initiate JobDetail with job name, job group, and executable job class JobDetail jobDetail = new JobDetail("jobDetail2", "jobDetailGroup2", SimpleQuartzJob.class); // Initiate CronTrigger with its name and group name CronTrigger cronTrigger = new CronTrigger("cronTrigger", "triggerGroup2"); try { // setup CronExpression CronExpression cexp = new CronExpression("0/5 * * * * ?"); // Assign the CronExpression to CronTrigger cronTrigger.setCronExpression(cexp); } catch (Exception e) { e.printStackTrace(); } // schedule a job with JobDetail and Trigger scheduler.scheduleJob(jobDetail, cronTrigger); // start the scheduler scheduler.start(); } |
Advanced quartz
As shown above, you can access a large number of features with only jobs and triggers. However, quartz is a rich and flexible scheduling package. For those who are willing to study it, it also provides more functions. The next section describes some advanced features of quartz.
Job warehouse
Quartz provides two different methods to store data related to jobs and triggers in the memory or database. The first method isRAMJobStore
Class, which is the default setting. This job repository is the easiest to use and provides the best performance because all data is stored in the memory. The main disadvantage of this method is the lack of data persistence. Because data is stored in Ram, all information is lost when an application or system crashes.
To solve this problem, quartz providesJDBCJobStore
. As the name suggests, the job warehouse stores all data in the database through JDBC. The cost of data persistence is reduced performance and increased complexity.
Set jdbcjobstore
As shown in the previous exampleRAMJobStore
Instance status. Because it is the default job repository, it can be used without additional settings. HoweverJDBCJobStore
Some Initialization is required.
Set and use in the ApplicationJDBCJobStore
Two steps are required: First, you must create a database table used by the job warehouse. JDBCJobStore
Compatible with all mainstream databases, and quartz provides a series of SQL scripts for creating tables, which can simplify the setting process. You can find the SQL script for creating a table in the "docs/dbtables" directory of the quartz release package. Second, some attributes must be defined, as shown in table 1:
Table 1. jdbcjobstore attributes
Attribute name |
Value |
Org. Quartz. jobstore. Class |
Org. Quartz. impl. jdbcjobstore. jobstoretx (or jobstorecmt) |
Org. Quartz. jobstore. tableprefix |
Qrtz _ (optional, customizable) |
Org. Quartz. jobstore. driverdelegateclass |
Org. Quartz. impl. jdbcjobstore. stdjdbcdelegate |
Org. Quartz. jobstore. datasource |
Qzds (customizable) |
Org. Quartz. datasource. qzds. Driver |
Com. IBM. db2.jcc. db2driver (cocould be any other database driver) |
Org. Quartz. datasource. qzds. url |
JDBC: DB2: // localhost: 50000/qz_smpl (customizable) |
Org. Quartz. datasource. qzds. User |
Db2inst1 (place userid for your own dB) |
Org. Quartz. datasource. qzds. Password |
Pass4dbadmin (place your own password for user) |
Org. Quartz. datasource. qzds. maxconnections |
30 |
Listing 4 showsJDBCJobStore
Data persistence. As in the previous exampleSchedulerFactory
AndScheduler
Start. Then, you no longer need to initialize the job and trigger. Instead, you need to obtain the trigger group name list, and then obtain the trigger name list for each group name. Note that each existing job should useScheduler.reschedule()
Method. Only re-initializing jobs terminated when the previous application was running does not correctly load the trigger attributes.
Listing 4. jdbcjobstorerunner. Java
public void task() throws SchedulerException { // Initiate a Schedule Factory SchedulerFactory schedulerFactory = new StdSchedulerFactory(); // Retrieve a scheduler from schedule factory Scheduler scheduler = schedulerFactory.getScheduler(); String[] triggerGroups; String[] triggers; triggerGroups = scheduler.getTriggerGroupNames(); for (int i = 0; i < triggerGroups.length; i++) { triggers = scheduler.getTriggerNames(triggerGroups[i]); for (int j = 0; j < triggers.length; j++) { Trigger tg = scheduler.getTrigger(triggers[j], triggerGroups[i]); if (tg instanceof SimpleTrigger && tg.getName().equals("simpleTrigger")) { ((SimpleTrigger)tg).setRepeatCount(100); // reschedule the job scheduler.rescheduleJob(triggers[j], triggerGroups[i], tg); // unschedule the job //scheduler.unscheduleJob(triggersInGroup[j], triggerGroups[i]); } } } // start the scheduler scheduler.start(); } |
Run jdbcjobstore
In the first running example, the trigger is initialized in the database. Figure 1 shows the database status after the trigger initialization but before the trigger is triggered. Therefore, based onsetRepeatCount()
Method, SetREPEAT_COUNT
Set to 100, whileTIMES_TRIGGERED
It is 0. After the application is running for a period of time, the application stops.
Figure 1. Data in the database when using jdbcjobstore (before running)
Figure 2 shows the status of the database after the application is stopped. In this figure,TIMES_TRIGGERED
Set to 19, indicating the number of job runs.
Figure 2. After 19 iterations of the same data
When you start the application again,REPEAT_COUNT
Updated. This is obvious in figure 3. As shown in figure 3REPEAT_COUNT
Is updated to 81, so the newREPEAT_COUNT
EqualREPEAT_COUNT
Value minus the previousTIMES_TRIGGERED
Value. In addition, the newTIMES_TRIGGERED
The value is 7, indicating that the job has been triggered seven times since the application was restarted.
Figure 3. Data after 7 iterations of 2nd running
After the application is stopped again,REPEAT_COUNT
Value is updated again. 4. The application has stopped and has not restarted. Similarly,REPEAT_COUNT
Value updated to the previous oneREPEAT_COUNT
Value minus the previous oneTIMES_TRIGGERED
Value.
Figure 4. Initial data before the trigger is run again
Usage attributes
As in useJDBCJobStore
You can use many attributes to adjust the quartz behavior. Should be inQuartz. PropertiesFile. See references for a list of configurable properties. Listing 5 showsJDBCJobStore
Attributes of the example:
Listing 5. Quartz. Properties
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 10 org.quartz.threadPool.threadPriority = 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true # Using RAMJobStore ## if using RAMJobStore, please be sure that you comment out the following ## - org.quartz.jobStore.tablePrefix, ## - org.quartz.jobStore.driverDelegateClass, ## - org.quartz.jobStore.dataSource #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore # Using JobStoreTX ## Be sure to run the appropriate script(under docs/dbTables) first to create tables org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX # Configuring JDBCJobStore with the Table Prefix org.quartz.jobStore.tablePrefix = QRTZ_ # Using DriverDelegate org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate # Using datasource org.quartz.jobStore.dataSource = qzDS # Define the datasource to use org.quartz.dataSource.qzDS.driver = com.ibm.db2.jcc.DB2Driver org.quartz.dataSource.qzDS.URL = jdbc:db2://localhost:50000/dbname org.quartz.dataSource.qzDS.user = dbuserid org.quartz.dataSource.qzDS.password = password org.quartz.dataSource.qzDS.maxConnections = 30 |