OSWorkflow and the Quartz Task Scheduler

0
109
9 min read

Task Scheduling with Quartz

Both people-oriented and system-oriented BPM systems need a mechanism to execute tasks within an event or temporal constraint, for example, every time a state change occurs or every two weeks. BPM suites address these requirements with a job-scheduling component responsible for executing tasks at a given time. OSWorkflow, the core of our open-source BPM solution, doesn’t include these temporal capabilities by default. Thus, we can enhance OSWorkflow by adding the features present in the Quartz open-source project.

What is Quartz?

Quartz is a Java job-scheduling system capable of scheduling and executing jobs in a very flexible manner. The latest stable Quartz version is 1.6. You can download Quartz from http://www.opensymphony.com/quartz/download.action.

Installing

The only file you need in order to use Quartz out of the box is quartz.jar. It contains everything you need for basic usage. Quartz configuration is in the quartz. properties file, which you must put in your application’s classpath.

Basic Concepts

The Quartz API is very simple and easy to use. The first concept that you need to be familiar with is the scheduler. The scheduler is the most important part of Quartz, managing as the word implies the scheduling and unscheduling of jobs and the firing of triggers.

Task Scheduling with Quartz

A job is a Java class containing the task to be executed and the trigger is the temporal specification of when to execute the job. A job is associated with one or more triggers and when a trigger fires, it executes all its related jobs. That’s all you need to know to execute our Hello World job.

Integration with OSWorkflow

By complementing the features of OSWorkflow with the temporal capabilities of Quartz, our open-source BPM solution greatly enhances its usefulness. The Quartz-OSWorkflow integration can be done in two ways—Quartz calling OSWorkflow workflow instances and OSWorkflow scheduling and unscheduling Quartz jobs. We will cover the former first, by using trigger-functions, and the latter with the ScheduleJob function provider.

Creating a Custom Job

Job’s are built by implementing the org.quartz.Job interface as follows:

     public void execute(JobExecutionContext context) throws 
JobExecutionException;

The interface is very simple and concise, with just one method to be implemented. The Scheduler will invoke the execute method when the trigger associated with the job fires. The JobExecutionContext object passed as an argument has all the context and environment data for the job, such as the JobDataMap.

The JobDataMap is very similar to a Java map but provides strongly typed put and get methods. This JobDataMap is set in the JobDetail file before scheduling the job and can be retrieved later during the execution of the job via the JobExecutionContext‘s getJobDetail().getJobDataMap() method.

Trigger Functions

trigger-functions are a special type of OSWorkflow function designed specifically for job scheduling and external triggering. These functions are executed when the Quartz trigger fires, thus the name. trigger-functions are not associated with an action and they have a unique ID. You shouldn’t execute a trigger-function in your code.

To define a trigger-function in the definition, put the trigger-functions declaration before the initial-actions element.

     ... 
<trigger-functions>
<trigger-function id="10">
<function type="beanshell">
<arg name="script">
propertySet.setString("triggered", "true");
</arg>
</function>
</trigger-function>
</trigger-functions>
<initial-actions>
...

This XML definition fragment declares a trigger-function (having an ID of 10), which executes a beanshell script. This script will put a named property inside the PropertySet of the instance but you can define a trigger-function just like any other Java- or BeanShell-based function.

To invoke this trigger-function, you will need an OSWorkflow built-in function provider to execute trigger-functions and to schedule a custom job—the ScheduleJob FunctionProvider.

More about Triggers

Quartz’s triggers are of two types—the SimpleTrigger and the CronTrigger. The former, as its name implies, serves for very simple purposes while the latter is more complex and powerful; it allows for unlimited flexibility for specifying time periods.

SimpleTrigger

SimpleTrigger is more suited for job firing at specific points in time, such as Saturday 1st at 3.00 PM, or at an exact point in time repeating the triggering at fixed intervals. The properties for this trigger are the shown in the following table:

s. The properties for this trigger are the shown in the following table:

Property

Description

Start time

The fire time of the trigger.

End time

The end time of the trigger. If it is specified, then it overrides the repeat count.

Repeat interval

The interval time between repetitions. It can be 0 or a positive integer. If it is 0, then the repeat count will happen in parallel.

Repeat count

How many times the trigger will fire. It can be 0, a positive integer, or SimpleTrigger.REPEAT_INDEFINITELY.

 

 

CronTrigger

The CronTrigger is based on the concept of the UN*X Cron utility. It lets you specify complex schedules, like every Wednesday at 5.00 AM, or every twenty minutes, or every 5 seconds on Monday. Like the SimpleTrigger, the CronTrigger has a start time property and an optional end time.

A CronExpression is made of seven parts, each representing a time component:

OSWorkflow and the Quartz Task Scheduler

Each number represents a time part:

  • 1 represents seconds
  • 2 represents minutes
  • 3 represents hours
  • 4 represents the day-of-month
  • 5 represents month
  • 6 represents the day-of-week
  • 7 represents year (optional field)

Here are a couple of examples of cron expression:

0 0 6 ? * MON: This CronExpression means “Every Monday at 6 AM”.

0 0 6 * *: This CronExpression mans “Every day at 6 am”.

For more information about CronExpressions refer to the following website:

http://www.opensymphony.com/quartz/wikidocs/CronTriggers%20Tutorial.html.

Scheduling a Job

We will get a first taste of Quartz, by executing a very simple job. The following snippet of code shows how easy it is to schedule a job.

    SchedulerFactory schedFact = new 
                                            org.quartz.impl.StdSchedulerFactory();
    Scheduler sched = schedFact.getScheduler();
    sched.start();
       JobDetail jobDetail = new JobDetail("myJob", null, HelloJob.class);
       Trigger trigger = TriggerUtils.makeHourlyTrigger();
                                                      // fire every hour
       trigger.setStartTime(TriggerUtils.getEvenHourDate(new Date()));
                                                    // start on the next even hour
       trigger.setName("myTrigger");
       sched.scheduleJob(jobDetail, trigger);

The following code assumes a HelloJob class exists. It is a very simple class that implements the job interface and just prints a message to the console.

    package packtpub.osw; 
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Hello world job.
*/
public class HelloJob implements Job
{
public void execute(JobExecutionContext ctx) throws
JobExecutionException
{
System.out.println("Hello Quartz world.");
}
}

 

The first three lines of the following code create a SchedulerFactory, an object that creates Schedulers, and then proceed to create and start a new Scheduler.

    SchedulerFactory schedFact = new 
org.quartz.impl.StdSchedulerFactory();
Scheduler sched = schedFact.getScheduler();
sched.start();

This Scheduler will fire the trigger and subsequently the jobs associated with the trigger. After creating the Scheduler, we must create a JobDetail object that contains information about the job to be executed, the job group to which it belongs, and other administrative data.

    JobDetail jobDetail = new JobDetail("myJob", null, HelloJob.class); 
This JobDetail tells the Scheduler to instantiate a HelloJob object
when appropriate, has a null JobGroup, and has a Job name of "myJob".
After defining the JobDetail, we must create and define the Trigger,
that is, when the Job will be executed and how many times, etc.
Trigger trigger = TriggerUtils.makeHourlyTrigger();
// fire every hour
trigger.setStartTime(TriggerUtils.getEvenHourDate(new Date()));
// start on the next even hour
trigger.setName("myTrigger");

The TriggerUtils is a helper object used to simplify the trigger code. With the help of the TriggerUtils, we will create a trigger that will fire every hour. This trigger will start firing the next even hour after the trigger is registered with the Scheduler. The last line of code puts a name to the trigger for housekeeping purposes.

Finally, the last line of code associates the trigger with the job and puts them under the control of the Scheduler.

    sched.scheduleJob(jobDetail, trigger);

When the next even hour arrives after this line of code is executed, the Scheduler will fire the trigger and it will execute the job by reading the JobDetail and instantiating the HelloJob.class. This requires that the class implementing the job interface must have a no-arguments constructor.

An alternative method is to use an XML file for declaring the jobs and triggers. This will not be covered in the book, but you can find more information about it in the Quartz documentation.

Scheduling from a Workflow Definition

The ScheduleJob FunctionProvider has two modes of operation, depending on whether you specify the jobClass parameter or not. If you declare the jobClass parameter, ScheduleJob will create a JobDetail with jobClass as the class implementing the job interface.

    <pre-functions> 
<function type="class">
<arg name="class.name">com.opensymphony.workflow.util.ScheduleJob
</arg>
<arg name="jobName">Scheduler Test
</arg>
<arg name="triggerName">SchedulerTestTrigger</arg>
<arg name="triggerId">10
</arg>
<arg name="jobClass">packtpub.osw.SendMailIfActive
</arg>
<arg name="schedulerStart">true
</arg>
<arg name="local">true
</arg>
</function>
</pre-functions>

This fragment will schedule a job based on the SendMailIfActive class with the current time as the start time. The ScheduleJob like any FunctionProvider can be declared as a pre or a post function.

On the other hand, if you don’t declare the jobClass, ScheduleJob will use the WorkflowJob.class as the class implementing the job interface. This job executes a trigger-function on the instance that scheduled it when fired.

   
<pre-functions>
<function type="class">
<arg name="class.name">com.opensymphony.workflow.util.ScheduleJob
</arg>
<arg name="jobName">Scheduler Test
</arg>
<arg name="triggerName">SchedulerTestTrigger
</arg>
<arg name="triggerId">10
</arg>
<arg name="schedulerStart">true
</arg>
<arg name="local">true
</arg>
</function>
</pre-functions>

 

This definition fragment will execute the trigger-function with ID 10 as soon as possible, because no CronExpression or start time arguments have been specified.

This FunctionProvider has the arguments shown in the following table:

LEAVE A REPLY

Please enter your comment!
Please enter your name here