Integration of Quartz with OSWorkflow
The first step in integrating OSWorkflow with Quartz is to change the way you think about jobs. You need to think in a totally different way when incorporating OSWorkflow into your Quartz applications. That's not to say that your current thinking is bad or incorrect, but using workflow with Quartz forces you to develop some new ideas about what constitutes a job. What you used to think of as a job will now become an OSWorkflow function. You can think of the logic that you had in your old jobs essentially as steps in the workflow. You'll still need and use Quartz jobs, but when integrating workflow with the Quartz framework, a Quartz job will initiate the workflow. When the workflow is running, the job will wait for it to finish.
Earlier in this chapter, when we talked about chaining jobs, each job represented a separate task. Job X executed and performed a task, and then called on Job Y to perform a somewhat related but separate task. There must have been some dependency between the two tasks, or you wouldn't have chained them together.
When adding workflow to this process, those individual jobs turn into steps within the workflow, and you need to create only a single job. When notified by the Scheduler, that job starts the workflow and then waits for the workflow to complete. This has some serious implications. The good news is tha by using OSWorkflow, you end up with fewer Quartz jobs because what used to be jobs are now steps (actually functions). The bad news is that if you have lots of jobs already created, it might take some work to convert jobs into OSWorkflow functions.
Downloading and Installing OSWorkflow
You can download the full distribution of OSWorkflow from its home on the OpenSymphony site at www.opensymphony.com/osworkflow. Grab the OSWorkflow binary from the distribution's root directory and also the third-party libraries within the /lib/core directory. Drop these binaries into the lib directory of your project. This should be the same directory where the Quartz binary is already located.
You must create a couple configuration files and place them in your classes directory. The first configuration file you need to create is called osworkflow.xml. This file is loaded at OSWorkflow startup and configures the runtime environment. The file for our example is shown in Listing 14.8.
Listing 14.8. The osworkflow.xml File Is Used to Configure the Runtime Environment for OSWorkflow
|
If you review the OSWorkflow documentation, you will find that you can choose from different types of persistence stores and workflow factories. The ones used in Listing 14.8 are the simplest and work fine for our examples.
The workflow factory class configured in Listing 14.8 is called the XMLWorkflowFactory, and it includes a property called resource. The XMLWorkflowFactory is used to load a resource file that contains all the available workflows. In this case, the resource property has a value of workflows.xml. You are allowed to have as many different workflows as you want. Each workflow will reside in a separate XML file, but you need some way of specifying the list of workflows that are available to the OSWorkflow engine. Because we have specified the factory as an XMLWorkflowFactory, the framework looks in the workflows.xml file to get the list of available workflows to load. Listing 14.9 shows the workflows.xml file for our example.
Listing 14.9. The workflows.xml File Defines the List of Workflows Available to the Application
|
Listing 14.9 lists only one workflow: data-import, which will be used to reference when we start the workflow. The actual workflow definition is stored in the file data-import-workflow.xml. Listing 14.10 shows the data-import workflow.
Listing 14.10. The data-import Workflow Is Defined in an XML File
org.cavaness.quartzbook.chapter14.ReadFileFunction org.cavaness.quartzbook.chapter14.SendEmailFunction |
The workflow definition in Listing 14.10 contains two steps: "read files" and "send e-mail notification. When the workflow is started, the initial-actions section is executed, and it calls step 1. When step 1 is entered, the execute() method in org.cavaness.quartzbook.chapter14.ReadFileFunction is called. Listing 14.11 shows this function.
Listing 14.11. The Workflow Engine Calls the ReadFileFunction During step 1
public class ReadFileFunction implements FunctionProvider { static Log logger = LogFactory.getLog(ReadFileFunction.class); public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException { logger.info("Entered " + this.getClass().getName()); // Read the files and process the data String dirName = (String)transientVars.get("SCAN_DIR"); if ( dirName == null ) { throw new InvalidInputException( "Scan dir not set" ); } File dir = new File( dirName ); File[] files = dir.listFiles(); int fileCount = files.length; ps.setInt( "FILE_COUNT", fileCount ); } } |
When the execute() method of the ReadFileFunction completes, the workflow transitions to step 2. In step 2, the org.cavaness.quartzbook.chapter14.SendEmailFunction is called and given a chance to execute. The SendEmailFunction is shown in Listing 14.12.
Listing 14.12. The SendEmailFunction Is Called During Step 2 of the Workflow
public class SendEmailFunction implements FunctionProvider { static Log logger = LogFactory.getLog(SendEmailFunction.class); public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException { logger.info("Entered " + this.getClass().getName()); int fileCount = ps.getInt("FILE_COUNT"); logger.info( "File count " + fileCount ); // Email creation code not shown } } |
We've obviously left out the implementation for our functions; it wouldn't have added anything to the discussion. We assume that you know how to send an e-mail using JavaMail.
Creating a Workflow Job
|