四. Quartz 与 OSWorkflow 的集成
OSWorkflow 与 Quartz 集成的第一步是要改变关于 Job 的思维方式。当把 OSWorkflow 引入到你的 Quartz 应用时你需要以完全不同的方式来思考。那也不是说你当前的想法就是糟糕的或不正确的,只是与 Quartz 一同用工作流强迫你生发一些关于是什么组成 Job 的新的思维。你过去概念中的 Job 现成变成了一个 OSWorkflow 函数。你可以认为是你原有 Job 实质上存在的逻辑作为工作流中的步骤。你仍然需要使用 Quartz 的 Job,但是,当与 Quartz 框架集成工作流时,一个 Quartz Job 将用来初始化工作流。在工作流运行时,这个 Job 将会等待它直至结束。
在本章前面部分,当我们谈到串联 Job 时,每个 Job 代表了一个独立的任务。Jox X 执行后并完成一个任务,接着通知 Job Y 去执行一个有点关联却是独立的任务。在这两个任务间必须有一些依赖关系,否则你不能把它们链接在一起。
当加入工作流到这个流程中时,那些独立的 Job 就变身为工作流中的步骤了,而且你仅需要创建单个的 Job。当收到 Scheduler 的通知时,那个 Job 就起过工作流然后等待着工作流的完成。这其中有一些严格的寓意。好消息是,通过使用 OSWorkflow,你只需更少的 Quartz Job,因为早先的 Job 现在成了步骤(实际是函数)。坏消息是假如你有创建了大量的 Job,将要耗费一些工作量去转换 Job 到 OSWorkflow 的函数。
·下载和安装 OSWorkflow
你可以从它在 OpenSymphony 站点 http://www.opensymphony.com/osworkflow 的主页下载完整的发行版。从发布页的根目录获取到二进制版的 OSWorkflow 和其他第三方的库,它们在 <OSWORKFLOW_DISTRIBUTION>/lib/core 目录下。把这些二进制包扔到你的项目的 lib 目录中。这应当和 Quartz 二进制包所在的同一目录。
你必须建立两个配置文件并放置到你的 classes 目录中。第一个你需要创建的配置文件叫做 osworkflow.xml。这个文件在 OSWorkflow 启动并配置运行时环境时被加载。我们例子中的该文件如代码 14.8 中显示。
代码 14.8. osworkfllow.xml 文件用于配置 OSWorkflow 的运行时环境
1 2 3 4 5 6 7 |
<osworkflow> <persistence class="com.opensymphony.workflow.spi.memory.MemoryWorkflowStore"/> <factory class="com.opensymphony.workflow.loader.XMLWorkflowFactory"> <property name="resource" value="workflows.xml" /> </factory> </osworkflow> |
假如你再看看 OSWorkflow 的文档,你会发现你可以从多种类型的持久性存储和工作流工厂中作出选择。在代码 14.8 中所用的都是最简单的并且为我们的例子工作的很好的。
配置在代码 14.8 中的工作流工厂类叫做 XMLWorkflowFactory,它包括一个称为 resource 的属性。XMLWorkflowFactory 用于加载一个包含了所有工作流的资源文件。在这里的 resource 属性的值是 workflows.xml。只要你想要多少个,就允许你可以有多少个不同的工作流。每个工作流驻留在一个单独的 XML 文件中,但是你需要以某种方式指定可用的工作流列表给 OSWorkflow 引擎。因为我们已经指定工厂为 XMLWorkflowFactory,所以框架会查看 workflows.xml 文件来得到可用的工作流并加载它们。代码 14.9 显示了我们例子里的 workflows.xml 文件。
代码 14.9. workflows.xml 文件定义了应用可用的工作流列表
1 2 3 4 5 6 |
<workflows> <workflow name="data-import" type="resource" location="data-import-workflow.xml"/> </workflows> |
代码 14.9 只列了一个工作流:data-import,它将在我们启动工作流时用作参考。实际的工作流定义是破存储在文件 data-import-workflow.xml 中的。代码 14.10 显示了 data-import 工作流。
代码 14.10. data-import 工作流被定义在一个 XML 文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.7//EN" "http://www.opensymphony.com/osworkflow/workflow_2_7.dtd"> <workflow> <initial-actions> <action id="1" name="Start Workflow"> <results> <unconditional-result old-status="Finished" status="Queued" step="1" /> </results> </action> </initial-actions> <steps> <step id="1" name="Read Files"> <actions> <action id="2" name="Get the files" auto="true"> <pre-functions> <function type="class"> <arg name="class.name"> org.cavaness.quartzbook.chapter14.ReadFileFunction </arg> </function> </pre-functions> <results> <unconditional-result old-status="Finished" status="Underway" step="2" /> </results> </action> </actions> </step> <step id="2" name="Send Email Notification"> <actions> <action id="3" name="Get the files" auto="true"> <pre-functions> <function type="class"> <arg name="class.name"> org.cavaness.quartzbook.chapter14.SendEmailFunction </arg> </function> </pre-functions> <results> <unconditional-result old-status="Finished" status="Underway" step="3" /> </results> </action> </actions> </step> <step id="3" name="finished" /> </steps> </workflow> |
定义在代码 14.10 的工作流包含两个步骤:“读文件” 和 "发 e-mail 通知"。当工作流启动后,initial-actions 块被执行,继而它调用步骤 1。当进入到步骤 1 后,org.cavaness.quartzbook.chapter14.ReadFileFunction 的 execute() 方法就被调用。代码 14.11 显示了这一函数。
代码 14.11. 工作流引擎在步骤 1 其间调用 ReadFileFunction
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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 ); } } |
当 ReadFileFunction 的 execute() 方法完成后,工作流会转换到步骤 2。在步骤 2,org.cavaness.quartzbook.chapter14.SendEmailFunction 被调用并获得机会执行。SendEmailFunction 显示在代码 14.12 中。
代码 14.12. SendEmailFunction 在工作流的步骤 2 期间被调用
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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 } } |
显然我们漏下了这个函数的实例;也没有对此多加以说明。我们假定你知道如何使用 JavaMail 发送电子邮件。
本文链接 https://yanbin.blog/quartz-job-scheduling-framework-14-3/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。