JBPM简介

第1章 介绍
JBOSS jBPM的是一个灵活、可扩展的工作流管理系统。JBOSS jBPM拥有直观的流程语言,用任务、异步的等待状态、定时器、自动化动作…等来表示业务流程图,把这些操作绑定在一起,JBOSS jBPM就拥有了非常强大和可扩展的控制流机制。
JBOSS jBPM只有最小的依赖,可以象使用java库一样非常容易的使用它。另外,也可以通过把它部署在J2EE集群应用服务器中,用在吞吐量极为关键的环境中。
JBOSS jBPM可被配置为任何数据库,并且可以部署到任何应用服务器。
1.1 概述
核心工作流和BPM功能被打包为一个简单的java库,这个库包括一个存储到数据库、从数据库更新和获取流程信息的服务。

图 1.1 JBOSS jBPM组件概观
1.2 JBOSS jBPM入门套件
入门套件是一个包含了所有jBPM组件的下载包,包括:
l        Jbpm-server,一个预配置好的jboss应用服务器。
l        Jbpm-designer,图形化设计jBPM流程的eclipse插件。
l        Jbpm-db,jBPM数据库兼容包(见下文)。
l        Jbpm,核心jbpm组件,包括库和本指南。
l        Jbpm-bpel,JBOSS jBPM BPEL扩展参考。
预配置好的JBOSS应用服务器安装了下列组件:
l        核心的jBPM组件,被打包为了一个服务档案。
l        一个包括jBPM库表的集成数据库:默认的hypersonic数据库包含了jBPM表,另外还包含一个流程。
l        jBPM控制台web应用程序,可以由流程参与者使用,也可以由jBPM管理员使用。
l        jBPM调度程序,用于定时器执行。调度程序在入门套件中被配置为一个servlet,这个servlet将产生一个线程来监视和执行定时器。
l        jBPM命令执行器,用于命令的异步执行。命令执行器也被配置为一个servlet,这个servlet将产生一个线程来监视和执行命令。
l        一个流程实例,已经被部署到了jBPM数据库中。
1.3 JBOSS jBPM图形化流程设计器
JBOSS jBPM还包括一个图形化设计工具,这个设计器是一个创作业务流程的图形化工具。
JBOSS jBPM图形化流程设计器是一个eclipse插件,可以独立安装的设计器已经在开发目标中。
图形化设计器非常重要的特性是它同时支持业务分析者和技术开发者,这使的业务流程建模可以平滑的转换到实际实现。
插件可以被用作本地更新设置(普通的zip文件),通过标准的eclipse软件更新机制安装。另外还有一个包,你可以直接把它解压到eclipse主目录里。
1.4 JBOSS jBPM核心组件
JBOSS jBPM核心组件是普通java(J2SE)软件,用来管理流程定义和流程实例执行的运行时环境。
JBOSS jBPM是一个java库,因此,它可以被用在任何java环境,例如web应用、swing应用、EJB、webservice…等,jBPM库还可以被打包为无状态会话EJB,这允许被作为集群部署,并且适用于极高的吞吐量。无状态会话EJB按照J2EE1.3规范编写,因此它可以部署到任何应用服务器。
JBOSS jBPM核心组件被打包为一个简单的java库文件,依赖于你所使用的功能,jbpm-3.0.jar库对第三方库有些依赖,如hibernate、dom4j和其他,这些在“第5章 部署”TODO中有清晰的说明。
为了持久化,jBPM内部使用hibernate,除了传统的O/R影射之外,hibernate还解决了不同数据库之间的SQL方言(dialect)问题,使jBPM可以方便的在当前所有数据库上移植。
JBOSS jPBM API可以从你的项目中任何定制的java软件中访问,例如你的web应用、你的EJB、你的webservice组件、你的消息驱动bean,或者其他java组件。
1.5 JBOSS jBPM控制台web应用程序
jBPM控制台web应用程序服务于两个目的。首先,它作为与由流程执行所产生的运行时任务相交互的一个重要的用户接口;其次,它是一个管理和监控控制台,允许检查和操纵运行时实例。
1.6 JBOSS jBPM身份组件
JBOSS jBPM可以与任何包含用户或其他组织信息目录的公司集成,但是对于没有组织信息组件可用的项目,JBOSS jBPM包含了它自己的组件。身份组件所使用的模型比传统的servlet、ejb和portlet(译者注:portlet是portal中最重要的组件,与servlet类似,portlet是部署在容器中用来生成动态内容的web组件。)模型更丰富。
更多信息,请看“11.11 身份组件”。TODO
1.7 JBOSS jBPM调度程序
JBOSS jBPM调度程序是一个用来监视和执行定时器的组件,它在流程执行期间被调度。
定时器组件软件被打包进核心的jbpm库,但是它需要被部署进下列环境之一:要么配置调度程序servlet去产生监视线程,要么用调度程序的main方法启动一个独立的JVM。
1.8 JBOSS jBPM数据库兼容包
JBOSS jBPM数据库兼容包是一个包含能使jBPM在你所选择的数据库上运行的所有信息、驱动程序和脚本的下载包。
1.9 JBOSS jBPM BPEL扩展
JBOSS jBPM BPEL 扩展是一个独立的扩展包,它扩展了jBPM,使之支持 BPEL(Business Process Execution Language商业流程执行语言),BPEL本质上是一个xml脚本语言,用来根据其他web服务(web services)编写web 服务(web services)。 
第3章 指南
这个指南将向你展示如何用jpdl创建基本的流程以及如何使用API管理运行期的执行。
这个指南的形式是解释一组示例,每个示例集中于一个特殊的主题,并且包含大量的注释,这些例子也可以在jBPM下载包的目录src/java.examples中找到。
最好的学习方法就是建立一个工程,并且通过在给定例子上做不同的变化进行实验。
对eclipse用户来说可以如下方式开始:下载jbpm-3.0-[version].zip并且解压到自己的系统,然后执行菜单“File”-->“Import…”-->“Existing Project into Workspace”,然后点击“Next”,浏览找到jBPM根目录,点击“Finish”。现在,在你的工作区中就有了一个jbpm.3工程,你可以在src/java.examples/…下找到本指南中的例子,当你打开这些例子时,你可以使用菜单“Run”-->“Run As…”-->“JUnit Test”运行它们。
jBPM包含一个用来创作例子中展示的XML的图形化设计器工具,你可以在“2.1 下载概述”中找到这个工具的下载说明,但是完成本指南不需要图形化设计器工具。
3.1 Hello World 示例
一个流程定义就是一个有向图,它由节点和转换组成。Hello world流程有三个节点,下面来看一下它们是怎样组装在一起的,我们以一个简单的流程作为开始,不用使用设计器工具,下图展示了hello world流程的图形化表示:


图 3.1 hello world流程图
public void testHelloWorldProcess() {
 // 这个方法展示了一个流程定义以及流程定义的执行。
 // 这个流程定义有3个节点:一个没有命名的开始状态,
 // 一个状态“s”,和一个名称为“end”的结束状态。
 // 下面这行是解析一段xml文本到ProcessDefinition对象(流程定义)。
 // ProcessDefinition把一个流程的规格化描述表现为java对象。
 ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
    "<process-definition>" +
    " <start-state>" +
    "    <transition to='s' />" +
    " </start-state>" +
    " <state name='s'>" +
    "    <transition to='end' />" +
    " </state>" +
    " <end-state name='end' />" +
    "</process-definition>"
 );
 
 // 下面这行是创建一个流程定义的执行。创建后,流程执行有一个
 // 主执行路径(=根令牌),它定位在开始状态。
 ProcessInstance processInstance =
      new ProcessInstance(processDefinition);
 
 // 创建后,流程执行有一个主执行路径(=根令牌)。
 Token token = processInstance.getRootToken();
 
 // 创建后,主执行路径被定位在流程定义的开始状态。
 assertSame(processDefinition.getStartState(), token.getNode());
 
 // 让我们开始流程执行,通过它的默认转换离开开始状态。
 token.signal();
 // signal方法将会把流程阻塞在一个等待状态。
 
 // 流程执行进入第一个等待状态“s”,因此主执行路径现在定位
 // 在状态“s”。 
 assertSame(processDefinition.getNode("s"), token.getNode());
 
 // 让我们发送另外一个信号,这将通过使用状态“s”的默认转换
 // 离开状态“s”,恢复流程执行。
 token.signal();
 // 现在signal方法将返回,因为流程示例已经到达结束状态。 
 assertSame(processDefinition.getNode("end"), token.getNode());
}
 
3.2 数据库示例
jBPM的特性之一就是在流程等待状态时,拥有把流程的执行持久化到数据库中的能力。下面的例子将向你展示怎样存储一个流程实例到数据库,例子中还会出现上下文。分开的方法被用来创建不同的用户代码,例如,一段代码在web应用中启动一个流程并且持久化执行到数据库,稍后,由一个消息驱动bean从数据库中加载流程实例并且恢复它的执行。
有关jBPM持久化的更多信息可以在“第7章 持久化”找到。
public class HelloWorldDbTest extends TestCase {
 
 static JbpmConfiguration jbpmConfiguration = null;
 
 static {
    // 在“src/config.files”可以找到象下面这样的一个示例配置文件。
    // 典型情况下,配置信息在资源文件“jbpm.cfg.xml”中,但是在这里
    // 我们通过XML字符串传入配置信息。   
    // 首先我们创建一个静态的JbpmConfiguration。一个JbpmConfiguration
    // 可以被系统中所有线程所使用,这也是为什么我们可以把它安全的设置
    // 为静态的原因。
 
    jbpmConfiguration = JbpmConfiguration.parseXmlString(
      "<jbpm-configuration>" +
     
      //jbpm-context机制分离了jbpm核心引擎和来自于外部环境的服务。
     
      " <jbpm-context>" +
      "    <service name='persistence' " +
      "             factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />" +
      " </jbpm-context>" +
     
      // 同样,jbpm使用的所有资源文件在jbpm.cfg.xml中被提供。
     
      " <string name='resource.hibernate.cfg.xml' " +
      "          value='hibernate.cfg.xml' />" +
      " <string name='resource.business.calendar' " +
      "          value='org/jbpm/calendar/jbpm.business.calendar.properties' />" +
      " <string name='resource.default.modules' " +
      "          value='org/jbpm/graph/def/jbpm.default.modules.properties' />" +
      " <string name='resource.converter' " +
      "          value='org/jbpm/db/hibernate/jbpm.converter.properties' />" +
      " <string name='resource.action.types' " +
      "          value='org/jbpm/graph/action/action.types.xml' />" +
      " <string name='resource.node.types' " +
      "          value='org/jbpm/graph/node/node.types.xml' />" +
      " <string name='resource.varmapping' " +
      "          value='org/jbpm/context/exe/jbpm.varmapping.xml' />" +
      "</jbpm-configuration>"
    );
 }
 
 public void setUp() {
    jbpmConfiguration.createSchema();
 }
 
 public void tearDown() {
    jbpmConfiguration.dropSchema();
 }
 
 public void testSimplePersistence() {
    // 在下面调用的3个方法之间,所有的数据通过数据库被传递。
    // 在这个测试中,这3个方法被依次执行,因为我们想要测试一个
    // 完整的流程情景。但是实际上,这些方法表示了对服务器的不同
    // 请求。   
    // 因为我们以一个干净的空数据库开始,所以我们首先必须部署流程。
    // 事实上,这只需要由流程开发者做一次。
    deployProcessDefinition();
 
    // 假设在一个web应用中当用户提交一个表单时我们起动一个流程
    // 实例(=流程执行)…
    processInstanceIsCreatedWhenUserSubmitsWebappForm();
 
    // 然后,一个异步消息到达时继续执行。
    theProcessInstanceContinuesWhenAnAsyncMessageIsReceived();
 }
 
 public void deployProcessDefinition() {
    // 这个测试展示了一个流程定义以及流程定义的执行。
  // 这个流程定义有3个节点:一个没有命名的开始状态,
 // 一个状态“s”,和一个名称为“end”的结束状态。
    ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
      "<process-definition name='hello world'>" +
      " <start-state name='start'>" +
      "    <transition to='s' />" +
      " </start-state>" +
      " <state name='s'>" +
      "    <transition to='end' />" +
      " </state>" +
      " <end-state name='end' />" +
      "</process-definition>"
    );
 
    // 查找在上面所配置的pojo持久化上下文创建器。
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {
      // 部署流程定义到数据库中。
      jbpmContext.deployProcessDefinition(processDefinition);
 
    } finally {
      // 关闭pojo持久化上下文。这包含激发(flush)SQL语句把流程
      // 定义插入到数据库。
      jbpmContext.close();
    }
 }
 
 public void processInstanceIsCreatedWhenUserSubmitsWebappForm() {
    // 本方法中的代码可以被放在struts的actiong中,或JSF管理
    //的bean中。
 
    //查找在上面所配置的pojo持久化上下文创建器。
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {
 
      GraphSession graphSession = jbpmContext.getGraphSession();
     
      ProcessDefinition processDefinition =
          graphSession.findLatestProcessDefinition("hello world");
   
      // 使用从数据库中获取的流程定义可以创建一个流程定义的执行
      // 就象在hello world例子中那样(该例没有持久化)。
      ProcessInstance processInstance =
          new ProcessInstance(processDefinition);
     
      Token token = processInstance.getRootToken();
      assertEquals("start", token.getNode().getName());
      // 让我们起动流程执行
      token.signal();
      // 现在流程在状态 's'。
      assertEquals("s", token.getNode().getName());
     
      // 现在流程实例processInstance被存储到数据库,
      // 因此流程执行的当前状态也被存储到数据库。
      jbpmContext.save(processInstance);
      // 以后我们可以从数据库再取回流程实例,并且通过提供另外一个
      // 信号来恢复流程执行。
 
    } finally {
      // 关闭pojo持久化上下文。
      jbpmContext.close();
    }
 }
 
 public void theProcessInstanceContinuesWhenAnAsyncMessageIsReceived() {
// 本方法中的代码可以作为消息驱动bean的内容。
 
    // 查找在上面所配置的pojo持久化上下文创建器。
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {
 
      GraphSession graphSession = jbpmContext.getGraphSession();
      // 首先,我们需要从数据库中取回流程实例。
      // 有几个可选方法来分辨出我们在这里所要处理的流程实例。
      // 在这个简单的测试中,最容易的方式是查找整个流程实例列表,
      // 这里它应该只会给我们一个结果。
 
      // 首先,让我们查找流程定义。
     
      ProcessDefinition processDefinition =
          graphSession.findLatestProcessDefinition("hello world");
 
      // 现在我们搜索这个流程定义的所有流程实例。
      List processInstances =
          graphSession.findProcessInstances(processDefinition.getId());
     
      // 因为我们知道在这个单元测试中只有一个执行。
      // 在实际情况中, 可以从所到达的信息内容中提取processInstanceId
      // 或者由用户来做选择。
      ProcessInstance processInstance =
          (ProcessInstance) processInstances.get(0);
     
      // 现在我们可以继续执行。注意:processInstance 将委托信号
      // 到主执行路径(=根令牌)。
      processInstance.signal();
 
      // 在这个信号之后,我们知道流程执行应该到达了结束状态。
      assertTrue(processInstance.hasEnded());
     
      // 现在我们可以更新数据库中的执行状态。
      jbpmContext.save(processInstance);
 
    } finally {
      // 关闭pojo持久化上下文。
      jbpmContext.close();
    }
 }
}
 
3.3 上下文示例:流程变量
流程变量包含了流程执行期间的上下文信息,流程变量与一个java.util.Map相似,它影射变量名称和值,值是java对象,流程变量作为流程实例的一部分被持久化。为了让事情简单,在这里的例子中我们只是展示使用变量的API,而没有持久化。
有关变量的更多信息可以在“第10章 上下文”中找到。
// 这个例子仍然从hello world流程开始,甚至没有修改。
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
 "<process-definition>" +
 " <start-state>" +
 "    <transition to='s' />" +
 " </start-state>" +
 " <state name='s'>" +
 "    <transition to='end' />" +
 " </state>" +
 " <end-state name='end' />" +
 "</process-definition>"
);
 
ProcessInstance processInstance =
 new ProcessInstance(processDefinition);
 
// 从流程实例获取上下文实例,用来使用流程变量。
ContextInstance contextInstance =
 processInstance.getContextInstance();
 
// 在流程离开开始状态之前,我们要在流程实例的上下文中
// 设置一些流程变量。
contextInstance.setVariable("amount", new Integer(500));
contextInstance.setVariable("reason", "i met my deadline");
 
// 从现在开始,这些流程变量与流程实例相关联。现在展示由用户代码通过
// API访问流程变量,另外,这些代码也可以存在于动作或节点的实现中。
// 流程变量被作为流程实例的一部分也被存储到数据库中。
processInstance.signal();
 
// 通过contextInstance访问流程变量。
 
assertEquals(new Integer(500),
             contextInstance.getVariable("amount"));
assertEquals("i met my deadline",
             contextInstance.getVariable("reason"));
 
3.4 任务分配示例
下一个例子我们将向你展示怎样分配一个任务到用户。因为jBPM工作流引擎与组织模型是独立的,所以任何一种用来计算参与者的表达式语言都是有限制的,因此,你不得不指定一个AssignmentHandler实现,用来包含任务参与者的计算。
public void testTaskAssignment() {
 // 下面的流程基于hello world 流程。状态节点被一个task-node节点
 // 所替换。task-node JPDL中的一类节点,它表示一个等待状态并且产生
 // 将要完成的任务,这些任务在流程继续之前被执行。
 ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
    "<process-definition name='the baby process'>" +
    " <start-state>" +
   "    <transition name='baby cries' to='t' />" +
    " </start-state>" +
    " <task-node name='t'>" +
    "    <task name='change nappy'>" +
    "      <assignment class='org.jbpm.tutorial.taskmgmt.NappyAssignmentHandler' />" +
    "    </task>" +
    "    <transition to='end' />" +
    " </task-node>" +
    " <end-state name='end' />" +
    "</process-definition>"
 );
 
 // 创建一个流程定义的执行。
 ProcessInstance processInstance =
      new ProcessInstance(processDefinition);
 Token token = processInstance.getRootToken();
 
 // 让我们起动流程执行,通过默认转换离开开始状态
 token.signal();
 // signal方法将会把流程阻塞在一个等待状态,
 // 在这里,就是阻塞在task-node。
 assertSame(processDefinition.getNode("t"), token.getNode());
 
 // 当执行到达task-node,一个'change nappy'任务被创建,并且
 // NappyAssignmentHandler 被调用,来决定任务将要分配给谁。
 // NappyAssignmentHandler 返回'papa'。
 
 // 在一个实际环境中,将使用org.jbpm.db.TaskMgmtSession中的方法
 // 从数据库获取任务。因为我们不想在这个例子中包含复杂的持久化,
 // 所以我们仅使用这个流程实例的第一个任务实例(我们知道,在这个
 // 测试情景,只有一个任务)。
 TaskInstance taskInstance = (TaskInstance) 
      processInstance
        .getTaskMgmtInstance()
        .getTaskInstances()
        .iterator().next();
 
 // 现在我们检查taskInstance 是否真正的分配给了'papa'。
 assertEquals("papa", taskInstance.getActorId() );
 
 // 现在我们假设'papa'已经完成了他的职责,并且标示任务为已完成。
 taskInstance.end();
 // 因为这是要做的最后一个任务(也是唯一一个),所以任务的完成
 // 会触发流程实例的继续执行。
 
 assertSame(processDefinition.getNode("end"), token.getNode());
}
 
3.5 定制动作示例
动作是一种绑定你自己的定制代码到jBPM流程的机制。动作可以与它自己的节点(如果它们与流程的图形化表示是相关的)相关联。动作也可以被放置在事件上,如执行转换、离开节点或者进入节点;如果那样的话,动作则不是图形化表示的一部分,但是在流程执行运行时,当执行触发事件时,它们会被执行。
我们先看一下将要在我们的例子中使用的动作实现:MyActionHandler,这个动作处理实现实际上没有做任何事…仅仅是设置布尔变量isExecuted为true。变量isExecuted是一个静态变量,因此它可以在动作处理的内部访问(即内部方法中),也可以从动作(即在动作类上)验证它的值。
有关动作的更多信息可以在“9.5 动作”中找到。
// MyActionHandler 是一个在jBPM流程执行期间可以执行用户代码的类。
public class MyActionHandler implements ActionHandler {
 
 // 在每个测试之前(在 setUp方法中), isExecuted 成员被设置为false。
 public static boolean isExecuted = false; 
 
 // 动作将设置isExecuted为true,当动作被执行之后,单元测试会
 // 展示。
 public void execute(ExecutionContext executionContext) {
    isExecuted = true;
 }
}
就象前面所提示那样,在每个测试之前,我们将设置静态域MyActionHandler.isExecuted为false。
 // 每个测试都将以设置MyActionHandler的静态成员isExecuted
 // 为false开始。
 public void setUp() {
    MyActionHandler.isExecuted = false;
 }
我们将会在转换上开始一个动作。
public void testTransitionAction() {
    // 下面的流程与hello world 流程不同。我们在从状态“s”到
    // 结束状态的转换上增加了一个动作。这个测试的目的就是展示
    // 集成java代码到一个jBPM流程是多么的容易。
    ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
      "<process-definition>" +
      " <start-state>" +
      "    <transition to='s' />" +
      " </start-state>" +
      " <state name='s'>" +
      "    <transition to='end'>" +
      "     <action class='org.jbpm.tutorial.action.MyActionHandler' />" +
      "    </transition>" +
      " </state>" +
      " <end-state name='end' />" +
      "</process-definition>"
    );
   
    // 让我们为流程定义起动一个新的执行。
    ProcessInstance processInstance =
      new ProcessInstance(processDefinition);
   
    // 下面的信号会导致执行离开开始状态,进入状态“s”。
    processInstance.signal();
 
    // 这里我们展示MyActionHandler还没有被执行。
    assertFalse(MyActionHandler.isExecuted);
   // ... 并且执行的主路径被定位在状态“s”。
    assertSame(processDefinition.getNode("s"),
               processInstance.getRootToken().getNode());
   
    // 下面的信号将触发根令牌的执行,令牌将会执行带有动作的转换,
    // 并且在调用signal方法期间动作经会被执行token。
    processInstance.signal();
   
    // 我们可以看到MyActionHandler在调用signal方法期间被执行了。
    assertTrue(MyActionHandler.isExecuted);
 }
下一个例子展示了相同的动作,但是现在动作分别被放置在了enter-node和leave-node事件上。注意,节点与转换相比有更多的事件类型,而转换只有一个,因此动作要放置在节点上应该放入一个event元素中。
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
 "<process-definition>" +
 " <start-state>" +
 "    <transition to='s' />" +
 " </start-state>" +
 " <state name='s'>" +
 "    <event type='node-enter'>" +
 "      <action class='org.jbpm.tutorial.action.MyActionHandler' />" +
 "    </event>" +
 "    <event type='node-leave'>" +
 "      <action class='org.jbpm.tutorial.action.MyActionHandler' />" +
 "    </event>" +
 "    <transition to='end'/>" +
 " </state>" +
 " <end-state name='end' />" +
 "</process-definition>"
);
 
ProcessInstance processInstance =
 new ProcessInstance(processDefinition);
 
assertFalse(MyActionHandler.isExecuted);
//下面的信号会导致执行离开开始状态,进入状态“s”,
// 因此状态 's' 被进入,动作被执行。
processInstance.signal();
assertTrue(MyActionHandler.isExecuted);
 
// 我们重新设置MyActionHandler.isExecuted。
MyActionHandler.isExecuted = false;
 
// 下一个信号将会触发执行离开状态's',因此动作将被执行。
processInstance.signal();
// 请看 
assertTrue(MyActionHandler.isExecuted);
 
第5章 部署
jBPM是一个嵌入式BPM引擎,这意味着你可以象安装一个独立的软件产品并集成一样把jBPM嵌入到你自己的java工程中,可以这样做的一个主要方面就是最小化的依赖,本章讨论jbpm库及其依赖。
5.1 Java运行环境
jBPM3要求J2SE1.4.2+
5.2 jBPM库
jbpm-[version].jar是核心功能库。
jbpm-identity-[version].jar是包含在“11.11 身份组件”中描述的身份组件的库(可选的)。
5.3 第三方库
在一个最小化的部署中,你仅仅通过放置commons-logging和dom4j库到你的classpath,就可以使用jBPM创建和运行流程,但是这样不支持流程的持久化。如果你不使用流程的xml解析,可以移除dom4j库,改为编程创建对象图。
表格 5.1
库 用途 描述 目录
commons-logging.jar 在jBPM和hibernate中记录日至。 jBPM代码日志记录到commons logging,commons logging库可以被配置为分发日志到java1.4日志、log4j、…等等,有关怎样配置commons logging的更多信息请看apache commons 用户指南。如果你使用log4j,最简单的方式就是把log4j库和一个log4j.properties放置到classpath,commons logging将会自动检测并使用该配置。 lib/jboss(从jboss4.0.3)
Dom4j-1.6.1.jar 流程定义和hibernate持久化。 Xml解析。 lib/dom4j
jBPM的典型部署中将包括流程定义和流程执行的持久化,在这种情况下,jBPM除了对hibernate及其所依赖库之外不再有任何其他依赖。
当然,hibernate所需要的库依赖于环境以及你将使用的特性,详细信息请查询hibernate文档。下表给出了一个普通POJO部署环境下的指示。
jBPM的发布使用hibernate3.1,但是它也可以使用3.0.x,如果那样的话,你不得不在hibernate.queries.hbm.xml配置文件中更新一些hibernate查询,有关自定义查询的更多信息,请看“7.6自定义查询”。
表格 5.2
库 用途 描述 目录
hibernate3.jar hibernate持久化。 最好的O/R映射器。 lib/hibernate(hibernate3.1)
antlr-2.7.5H3.jar 由hibernate持久化的查询分析使用。 解析库。 lib/jboss(从jboss4.0.3)
cglib-2.1_2jboss.jar hibernate持久化。 hibernate代理所使用的反射库。 lib/jboss(从jboss4.0.3)
Commons-collection.jar hibernate持久化。   lib/jboss(从jboss4.0.3)
ehcache-1.1.jar hibernate持久化(默认配置)。 二级缓存实现,当为hibernate配置不同的的缓存时,不需要本库。 lib/hibernate
jaxen-1.1-beta-4.jar 流程定义和hiberante持久化。 XPath库(由dom4j使用)。 lib/hibernate
jdbc2_0-stdext.jar hibernate持久化。   lib/hibernate
asm.jar hibernate持久化。 asm字节码库。 lib/hibernate
asm-attrs.jar hibernate持久化。 asm字节码库。 lib/hibernate
beanshell库是可选的,如果你不包含它,你将不能把beanshell集成到jbpm流程语言中使用,并且你将会得到一个日志信息说“jbpm不能加载Script类,因此script元素不能使用”。
表格 5.3
库 用途 描述 目录
bsh-1.3.0.1.jar beanshell脚本解释程序。 只用在script和decision元素中,当你不使用这些流程元素时,可以移除beanshell库,但是必须在hibernate.cfg.xml文件中注释掉Sceipt.hbm.xml映射行。 lib/jboss
 
 第6章 配置
jBPM配置由java类org.jbpm.JbpmConfiguration来描述,获取JbpmConfiguration的最简单方式是使用单态实例方法JbpmConfiguration.getInstance()。
如果你想从另外一个源加载配置,你可以使用JbpmConfiguration.parseXxxx方法。
static JbpmConfinguration jbpmConfiguration = JbpmConfinguration.getInstance();
JbpmConfiguration是线程安全的,因此可以在一个静态成员中维护,所有的线程都可以把JbpmConfiguration作为一个JbpmContext对象的工厂来使用。JbpmContext表示一个事务,在一个上下文块中JbpmContext使服务可用,上下文块如下:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
 // This is what we call a context block.
 // Here you can perform workflow operations
 
} finally {
 jbpmContext.close();
}
JbpmContext使一组服务和配置为jBPM可用,这些服务在jbpm.cfg.xml配置文件中被配置,并且使jBPM无论运行在任何java环境中这些服务都可用。
下面是JbpmContext的一个典型配置,就象你在src/config.files/jbpm.cfg.xml中看到的那样:
<jbpm-configuration>
 
 <jbpm-context>
    <service name='persistence' factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />
    <service name='message' factory='org.jbpm.msg.db.DbMessageServiceFactory' />
    <service name='scheduler' factory='org.jbpm.scheduler.db.DbSchedulerServiceFactory' />
    <service name='logging' factory='org.jbpm.logging.db.DbLoggingServiceFactory' />
    <service name='authentication' factory='org.jbpm.security.authentication.DefaultAuthenticationServiceFactory' />
 </jbpm-context>
 
 <!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
 <string name='resource.hibernate.cfg.xml' value='hibernate.cfg.xml' />
 <!-- <string name='resource.hibernate.properties' value='hibernate.properties' /> -->
 <string name='resource.business.calendar' value='org/jbpm/calendar/jbpm.business.calendar.properties' />
 <string name='resource.default.modules' value='org/jbpm/graph/def/jbpm.default.modules.properties' />
 <string name='resource.converter' value='org/jbpm/db/hibernate/jbpm.converter.properties' />
 <string name='resource.action.types' value='org/jbpm/graph/action/action.types.xml' />
 <string name='resource.node.types' value='org/jbpm/graph/node/node.types.xml' />
 <string name='resource.parsers' value='org/jbpm/jpdl/par/jbpm.parsers.xml' />
 <string name='resource.varmapping' value='org/jbpm/context/exe/jbpm.varmapping.xml' />
 
 <int name='jbpm.byte.block.size' value="1024" singleton="true" />
 <bean name='jbpm.task.instance.factory' class='org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl' singleton='true' />
 <bean name='jbpm.variable.resolver' class='org.jbpm.jpdl.el.impl.JbpmVariableResolver' singleton='true' />
 <long name='jbpm.msg.wait.timout' value='5000' singleton='true' />
 
</jbpm-configuration>
在这个配置中你可以看到三部分:
l        第一部分使用一组服务实现配置jbpm上下文,这些配置的可选项在以后描述特定服务实现的章节中做了描述。
l        第二部分是所有配置资源的引用映射,如果你想要定制某些配置文件,这些资源引用可以被修改。典型情况下,你可以在jbpm-3.x.jar中拷贝一个默认的配置,并且把它放在classpath中的某个位置,然后在这个文件中修改引用为你自己定制版本的配置文件。
l        第三部分是在jbpm中使用的一些别名配置,这些配置选项在包含特定主题的章节中做了描述。
缺省配置的一组服务被定位于一个简单的web应用环境和最小的依赖,持久化服务将获得一个jdbc连接,所有其他服务将会使用这个相同的连接来完成它们的服务,因此,工作流的所有操作都被集中到一个JDBC连接的一个事务中,不再需要事务管理器。
JbpmContext包含为大多流程操作所提供的方便方法:
 public void deployProcessDefinition(ProcessDefinition processDefinition) {...}
 public List getTaskList() {...}
 public List getTaskList(String actorId) {...}
 public List getGroupTaskList(List actorIds) {...}
 public TaskInstance loadTaskInstance(long taskInstanceId) {...}
 public TaskInstance loadTaskInstanceForUpdate(long taskInstanceId) {...}
 public Token loadToken(long tokenId) {...}
 public Token loadTokenForUpdate(long tokenId) {...}
 public ProcessInstance loadProcessInstance(long processInstanceId) {...}
 public ProcessInstance loadProcessInstanceForUpdate(long processInstanceId) {...}
 public ProcessInstance newProcessInstance(String processDefinitionName) {...}
 public void save(ProcessInstance processInstance) {...}
 public void save(Token token) {...}
 public void save(TaskInstance taskInstance) {...}
 public void setRollbackOnly() {...}
记住,XxxForUpdate方法将记录所加载的对象为自动保存的,因此不需要再调用save方法。
指定多个jbpm上下文是可能的,但是你必须确保每个jbpm上下文的name属性必须是唯一的,可以使用JbpmConfiguration.createContext(String name)获取命名的上下文。
service元素定义了一个服务名称和此服务的服务工厂,服务将只会在使用JbpmContext.getServices().getService(String name)请求时被创建。
工厂也可以被作为一个元素来指定,而不是用属性。那样可以用于在工厂对象中注入一些配置信息,负责解析XML的组件创建和装配被调用的对象工厂。
6.1 配置属性
jbpm.byte.block.size:文件附件和二进制变量被存储到数据库,不是作为blob,而是作为一个固定大小的二进制对象列表,这可以方便用于不同数据库并且提高jBPM的全面嵌入能力,这个参数控制了固定长度块的大小。
jbpm.task.instance.factory:定制任务实例的创建方式,在这个属性中指定一个全类名,当你想要定制TaskInstance bean并且向它添加新的属性时,这是有必要的。请参考“11.10 定制任务实例”,指定的类必须实现org.jbpm.tskmgmt.TaskInstanceFactory。
jbpm.variable.resolver:定制jBPM在JSF表达式中寻找第一个术语的方式。
jbpm.msg.wait.timout:定制消息缓存的时间。
6.2 配置文件
下面是对在jBPM中定义的所有配置文件的简短描述。
6.2.1 Hibernate.cfg.xml文件
这个文件包含hibernate配置,并且引用hibernate映射资源文件。
位置:hibernate.cfg.xml文件如果不另外在jbpm.properties文件的jbpm.hibernate.cfg.xml属性中指定,则jBPM工程中的默认hibernate配置文件在目录src/config.files/hibernate.cfg.xml。
6.2.2 Hibernate查询配置文件
这个文件包含jBPM会话org.jbpm.db.*Session中所使用的hibernate查询。
位置:org/jbpm/db/hibernate.queries.hbm.xml。
6.2.3 节点类型配置文件
这个文件包含了XML节点元素到节点实现类的映射。
位置:org/jbpm/graph/node/node.types.xml。
6.2.4 动作类型配置文件
这个文件包含了XML动作元素到动作实现类的映射。
位置:org/jbpm/graph/action/action.types.xml。
6.2.5 业务日历配置文件
包含了业务时间和空闲时间的定义。
位置:org/jbpm/calendar/jbpm.business.calendar.properties。
6.2.6 变量映射配置文件
指定了流程变量(java对象)的值怎样转换到用于存储到jbpm数据库中的变量实例。
位置:org/jbpm/context/exe/jbpm.varmapping.xml。
6.2.7 转换器配置文件
指定了id到类名的映射。id被存储到数据库,org.jbpm.db.hibernate.ConverterEnumType被用来映射id到单态对象。
位置:org/jbpm/db/hibernate/jbpm.converter.properties。
6.2.8 缺省模块配置文件
指定哪个模块被缺省添加到一个新的流程定义ProcessDefinition。
位置:org/jbpm/graph/def/jbpm.default.modules.properties。
6.2.9 流程档案解析器配置文件
指定流程档案解析的解析器。
位置:org/jbpm/jpdl/par/jbpm.parsers.xml。
6.3 对象工厂
对象工厂可以依照bean的xml配置文件创建对象,配置文件指定了对象将被怎样创建、配置以及组装到一起形成一个完整的对象图。对象工厂可以注入配置和其他bean到一个bean中。
在最简单的形式中,对象工厂可以从这样一个配置中创建基本类型和java bean:
<beans>
 <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance"/>
 <string name="greeting">hello world</string>
 <int name="answer">42</int>
 <boolean name="javaisold">true</boolean>
 <float name="percentage">10.2</float>
 <double name="salary">100000000.32</double>
 <char name="java">j</char>
 <null name="dusttodust" />
</beans>
 
---------------------------------------------------------
 
ObjectFactory of = ObjectFactory.parseXmlFromAbove();
assertEquals(TaskInstance.class, of.getNewObject("task").getClass());
assertEquals("hello world", of.getNewObject("greeting"));
assertEquals(new Integer(42), of.getNewObject("answer"));
assertEquals(Boolean.TRUE, of.getNewObject("javaisold"));
assertEquals(new Float(10.2), of.getNewObject("percentage"));
assertEquals(new Double(100000000.32), of.getNewObject("salary"));
assertEquals(new Character('j'), of.getNewObject("java"));
assertNull(of.getNewObject("dusttodust"));
你也可以配置列表:
<beans>
 <list name="numbers">
    <string>one</string>
    <string>two</string>
    <string>three</string>
 </list>
</beans>
以及map:
<beans>
 <map name="numbers">
    <entry><key><int>1</int></key><value><string>one</string></value></entry>
   <entry><key><int>2</int></key><value><string>two</string></value></entry>
    <entry><key><int>3</int></key><value><string>three</string></value></entry>
 </map>
</beans>
Bean可以使用直接成员注入和通过属性的setter。
<beans>
 <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
    <field name="name"><string>do dishes</string></field>
    <property name="actorId"><string>theotherguy</string></property>
 </bean>
</beans>
Bean可以被引用,被引用的对象不必必须是一个bean,也可以是一个字符串、整型或其他任何对象。
<beans>
 <bean name="a" class="org.jbpm.A" />
 <ref name="b" bean="a" />
</beans>
Bean可以使用任何构造器构造(构造函数)。
<beans>
 <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
    <constructor>
      <parameter class="java.lang.String">
        <string>do dishes</string>
      </parameter>
      <parameter class="java.lang.String">
        <string>theotherguy</string>
      </parameter>
    </constructor>
 </bean>
</beans>
…或者在bean上使用工厂方法…
<beans>
 <bean name="taskFactory"
         class="org.jbpm.UnexistingTaskInstanceFactory"
         singleton="true"/>
 
 <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
    <constructor factory="taskFactory" method="createTask" >
      <parameter class="java.lang.String">
        <string>do dishes</string>
      </parameter>
      <parameter class="java.lang.String">
        <string>theotherguy</string>
      </parameter>
    </constructor>
 </bean>
</beans>
…或者在类上使用一个静态工厂方法…
<beans>
 <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
    <constructor factory-class="org.jbpm.UnexistingTaskInstanceFactory" method="createTask" >
      <parameter class="java.lang.String">
        <string>do dishes</string>
      </parameter>
      <parameter class="java.lang.String">
        <string>theotherguy</string>
      </parameter>
    </constructor>
 </bean>
</beans>
每个命名的对象都可以使用属性singleton=“true”标记为单态,这意味着给定的对象工厂为每个请求将总是返回相同的对象。注意,单态不能在不同对象工厂之间共享。
单态特性导致getObject和getNewObject方法间的区别,对象工厂的典型用户将使用getNewObject,这意味着在一个新的对象图被构造之前对象对象工厂首先要清除对象缓存。在构造对象图期间,非单态对象被存储在对象工厂的对象缓存中,允许共享引用一个对象。单态对象缓存不同于普通对象缓存,单态对象缓存不需要清除,而普通对象缓存在每个getNewObject方法被调用起始会被清除。 第7章 持久化
在很多情况下,Jbpm需要维护跨越长时间的流程的执行,在这里,“长时间”意味着跨越几个处理事务。因为流程执行就像是状态机,在一个处理事务中,我们就是把流程执行状态机从一个状态转到下一个状态,所以持久化的主要目的就是在等待状态存储流程的执行。
一个流程定义可以表现为三种不同形式:XML、Java对象、Jbpm数据库中记录。执行(运行时)信息和日志信息可以表现为两种形式:Java对象和Jbpm数据库中的记录。

图 7.1 转换和不同形式
有关XML表示流程定义以及流程档案的更多信息,请参考“第16章Jbpm流程定义语言(JPDL)”。
关于怎样部署流程档案到数据库在“第16.1.1节部署流程档案” 中可以找到。
7.1 持久化API
7.1.1 相关配置框架
持久化API集成在了配置框架中,通过JbpmContext暴露出了一些便利方法,因此持久化API要像下面这样在JbpmContext块中使用:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
 
 // Invoke persistence operations here
 
} finally {
 jbpmContext.close();
}
在下文中,我们假设配置中包含如下持久化服务(如示例配置文件src/config.files/jbpm.cfg.xml):
<jbpm-configuration>
 
 <jbpm-context>
    <service name='persistence' factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />
    ...
 </jbpm-context>
 ...
</jbpm-configuration>
7.1.2 JbpmContext中的便利方法
三个普通的持久化操作是:
l       部署流程
l       启动一个流程的执行
l       继续一个执行
首先部署一个流程定义。典型的,这可以从图形化流程设计器或部署流程的Ant任务直接完成,但是在这里你会看到怎样通过编程实现:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
 ProcessDefinition processDefinition = ...;
 jbpmContext.deployProcessDefinition(processDefinition);
} finally {
 jbpmContext.close();
}
要创建一个新的流程执行,我们需要指定一个要实例化执行的流程定义,通常的作法是指定一个流程名称,并让Jbpm在数据库中查找这个流程的最新版本:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
 String processName = ...;
 ProcessInstance processInstance =
      jbpmContext.newProcessInstance(processName);
} finally {
 jbpmContext.close();
}
要继续一个流程执行,我们需要从数据库中取出这个流程实例、令牌或者任务实例,在这些POJO Jbpm对象上调用一些方法,然后再把对流程实例的更新保存到数据库中。
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
 long processInstanceId = ...;
 ProcessInstance processInstance =
      jbpmContext.loadProcessInstance(processInstanceId);
 processInstance.signal();
 jbpmContext.save(processInstance);
} finally {
 jbpmContext.close();
}
注意:如果你在JbpmContext中使用xxxForUpdate方法,则jbpmContext.save不需要再显式的调用,因为在jbpmContext关闭时它会被自动调用。举例来说,假设我们要通知Jbpm一个任务实例已经完成,(注意:任务实例完成能够触发继续执行,所以相联的流程实例必须保存。)一个非常方便的方法是使用loadTaskInstanceForUpdate方法:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
 long taskInstanceId = ...;
 TaskInstance taskInstance =
      jbpmContext.loadTaskInstanceForUpdate(taskInstanceId);
 taskInstance.end();
} finally {
 jbpmContext.close();
}
正象是后台信息一样,下面部分解释了Jbpm是怎样管理持久化和使用Hibernate的。
JbpmConfiguration维护了一组服务工厂(ServiceFactory),服务工厂象前面内容所说的在jbpm.cfg.xml中配置,并且是lazy(懒)实例化的。当在第一次需要DbpersistenceServiceFactory时它被实例化,然后服务工厂就被在JbpmConfiguration中维护,DbpersistenceServiceFactory管理一个hibernate SessionFactory,而hibernate SessionFactory也是在第一次被请求的时候才创建的。

图 7.2 持久化相关类
在调用jbpmConfiguration.createJbpmContext()方法期间,只有JbpmContext被创建,这时没有更多的持久化相关实例。JbpmContext管理一个DbPersistenceService,它在被第一次请求的时候实例化。DbPersistenceServece管理hibernate会话(session),hibernate会话在DbPersistenceServece中也是被Lazy(懒)创建的,因此结果是:一个hibernate会话只有在第一个请求持久化的操作被调用时才被打开,而在这之前不会。
7.1.3 高级API用法
DbPersistenceService维护了一个懒实例化的hibernate 会话,所有的数据库存取都是通过这个hibernate会话完成的。所有的查询和更新都是通过Jbpm暴露出的xxxSession类完成的,例如GraphSession、SchedulerSession、LoggingSession等等,这些session类都是提交hibernate查询并且都使用下层相同的hebernate会话。
XxxxSession类也是通过JbpmContext来获取的。
7.2 配置持久化服务
7.2.1 The hibernate session factory
默认情况下,DbpersistenceServiceFactory将使用classpath根下的资源文件hibernate.cfg.xml来创建hibernate会话工厂。注意:hibernate资源配置文件由“jbpm.hibernate.cfg.xml”属性指定,可以在jbpm.cfg.xml中定制。下面是默认配置:
<jbpm-configuration>
 ...
 <!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
 <string name='resource.hibernate.cfg.xml'
          value='hibernate.cfg.xml' />
 <!-- <string name='resource.hibernate.properties'
                  value='hibernate.properties' /> -->
 ...
</jbpm-configuration>
当属性“resource.hibernate.properties”被指定,则指定的资源文件中的属性将会覆盖hibernate.cfg.xml中的配置,因此我们可以使用hibernate.properties文件、而不是更新hibernate.cfg.xml文件来方便的更新Jbpm指向自己的数据库:hibernate.cfg.xml仅仅需要拷贝过去不需要做任何更改。
7.2.2 The DbPersistenceServiceFactory
DbpersistencrServiceFactory拥有三个配置属性:isTransactionEnabled、sessionFactoryJndiName和dataSourceJndiName。要指定这几个属性中的任何一个,你需要象bean一样在factory元素中定义服务工厂,如下:
 <jbpm-context>
    <service name="persistence">
      <factory>
        <bean factory="org.jbpm.persistence.db.DbPersistenceServiceFactory">
          <field name="isTransactionEnabled"><false /></field>
          <field name="sessionFactoryJndiName">
            <string value="java:/myHibSessFactJndiName" />
          </field>
          <field name="dataSourceJndiName">
            <string value="java:/myDataSourceJndiName" />
          </field>
        </bean>
      </factory>
    </service>
    ...
 </jbpm-context>
l        isTransactionEnabled:默认情况下,Jbpm将会开始并且结束hibernate事务。如果禁用事务并且禁止使用hibernate管理事务,可以象上例一样配置isTransactionEnabled属性为false。更多信息,请参考“第7.3节 hibernate事务” 。
l        sessionFactoryJndiName:默认情况下这个属性为空,意味着会话工厂不从JNDI获取。如果设置了,当需要使用会话工厂创建一个hibernate会话时,则将会从所提供的JNDI名称中通过jndi获取会话工厂。
l       dataSourceJndiName:默认情况下这个属性为空,JDBC连接的创建会委托给hibernate。通过指定一个datasource,当打开一个新的会话时,Jbpm将会从datasource中获取一个JDBC连接并且把这个连接提供给hibernate。有关用户提供JDBC连接,请参考“第7.5节用户提供的素材”。
7.3 Hibernate事务
默认情况下,Jbpm将委托事务到hibernate,并且使用每个事务一个会话的模式。Jbpm在hibernate会话被打开时启动一个hibernate事务,这在第一次调用jbpmContext的一个持久化操作时发生,事务会在hiberante会话关闭之前被提交,这在jbpmContext.close()中发生。
使用jbpmContext.setRollbackOnly()回滚一个事务,在这种情况下,事务会在jbpmContext.close()方法中关闭会话之前回滚。
要禁止Jbpm在hibernate API之上调用任何事务,可以设置isTransactionEnabled属性为false,就象上面的“第7.2.2 节 The DbPersistenceServiceFactory”说明的一样。
7.4 管理事务
当在J2EE应用服务器中(如JBOSS)使用Jbpm时,大多情况下需要管理事务。如:
l        在应用服务器中配置一个DataSource
l        配置hibernate使它的连接使用DataSource
l        使用容器管理事务
l       在Jbpm中禁用事务
7.5 用户提供的素材
你也可以编程向Jbpm提供JDBC连接、hibernate会话和hibernate会话工厂。
当这些资源提供给Jbpm时,Jbpm会使用所提供的资源,而不是配置中指定的。
JbpmContext类包含了一些方便的方法来实现通过编程注入资源。例如,向Jbpm提供一个JDBC连接,可以使用如下代码:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
 Connection connection = ...;
 jbpmContext.setConnection(connection);
 
 // invoke one or more persistence operations
 
} finally {
 jbpmContext.close();
}
JbpmContext类有如下方便的方法用来支持编程提供资源:
JbpmContext.setConnection(Connection);
JbpmContext.setSession(Session);
JbpmContext.setSessionFactory(SessionFactory);
7.6 自定义查询
Jbpm使用的所有HQL查询都集中在一个配置文件中,这个资源文件在hibernate.cfg.xml配置文件中被引用,如下:
<hibernate-configuration>
 ...
    <!-- hql queries and type defs -->
    <mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" />
 ...
</hibernate-configuration>
要自定义一个或更多查询,先拷贝一个原始文件并且把你自定义的版本放在classpath下的某个位置,然后在hibernate.cfg.xml中更新 “org/jbpm/db/hibernate.queries.hbm.xml”,使之指向你自己定义的版本。
7.7 数据库兼容
Jbpm可以在hibernate所支持的任何数据库上运行。
Jbpm中的示例配置文件(src/config.files)指定了使用的是hypersonicin in-memory数据库,这个数据库在开发和测试阶段是一个理想的选择,hypersonicin in-memory数据库把数据放在内存中而不是存储到磁盘。
7.7.1 改变Jbpm数据库
下面列出了当改变Jbpm使用一个不同数据库时的作法:
l        把jdbc驱动库放在classpath
l        改变Jbpm所使用的hibernate配置
l       在新的数据库中创建表
7.7.2 Jbpm数据库表
Jbpm.db子项目包含了一系列的驱动库、说明和脚本来帮助你在你所选择的数据库上开始工作。请查阅jbpm.dm工程根目录下的readme.html文件来获取更多信息。
尽管Jbpm有能力生成所有数据库的DDL脚本,但是这些不总是最优的,因此你可能想要你的DBA查阅这些生成的DDL,来进行列类型的优化和使用索引。
在开发中,你可能对下面的hibernate配置感兴趣:如果你设置hibernate配置属性“hibernate.hbm2ddl.auto”为“create-drop”(例如在hibernate.cfg.xml中),则在应用中第一次使用时,便会在数据库中自动创建,当应用关闭时,则表会被删除。
也可以编程调用jbpmConfiguration.createSchema()和jbpmConfiguration.dropSchema()方法来实现表的创建和删除。
7.8 结合你自己的hibernate类
在你自己的项目中,你可能使用hibernate来持久化,可以使你自己的持久化类与Jbpm持久化类结合,这是可选的。这样做有两个主要的好处:
首先,会话、连接和事务管理变得容易。通过结合Jbpm和你自己的持久化在一个hibernate会话工厂,则只有一个hibernate会话、一个jdbc连接来处理你自己的和Jbpm的持久化,因此,Jbpm的更新与你自己的域模型更新在相同的事务中,这可以排除再使用一个事务管理器。
其次,这毫无争议的就把你自己的hibernate持久化对象融入了流程变量中。
可以通过创建一个核心的hibernate.cfg.xml来很容易的与Jbpm持久化类集成你自己的持久化类,你可以使用Jbpm中的src/config.files/hibernate.cfg.xml作为开始,然后在其中添加你自己的hibernate映射文件的引用。
7.9 定制Jbpm的hibernate映射文件
你可以按照如下方式来定制任何Jbpm的hibernate映射文件:
l        从源码(src/java.jbpm/…)或Jbpm库中拷贝Jbpm的hibernate映射文件
l        把拷贝的文件放置到classpath下任何你想要放置的地方
l       在hibernate.cfg.xml配置文件中更新指向自定义的映射文件
7.10 二级缓存
流程定义加载一次之后,Jbpm使用hibernate的二级缓存来把它放在内存中,流程定义类和集合在Jbpm的hibernate映射文件中使用cache元素来配置,如下:
<cache usage="nonstrict-read-write"/>
因为流程定义不会改变(应该不会改变),所以把它们放在二级缓存中是很好的做法,请参考“第16.1.3节 改变已发布的流程定义”。
二级缓存是JBOSS Jbpm实现的一个很重要的方面,如果没有这个缓存,JBOSS Jbpm在与其它实现BPM引擎的技术进行比较时,会是一个严重的缺陷。
缓存策略设置为nonstrict-read-write。在运行时,缓存策略可以设置为read-only,在这种情况下,你需要一个单独的hibernate映射文件来部署流程,这也是为什么我们选择nonstrict-read-write的原因。

第9章 流程建模
9.1 综述
流程定义(process definition)基于有向图表示了一个业务流程的规格化描述。图是由节点(node)和转换(transition)组成的,图中每个节点都有一个特定类型,节点的类型定义了运行时的行为。一个流程定义只能有一个开始状态。
令牌(token)是一个执行路线。令牌是运行时概念,它维护了一个指向图中节点的指针。
流程实例是(process instance)流程定义的执行。当一个流程实例创建后,一个令牌也为执行的主要路线创建了,这个令牌被称为流程实例的根令牌(root token),它被定位于流程定义的开始状态。
信号(signal)指示令牌继续图的执行。当令牌接收到一个没有命名的信号,它会经由缺省的离开转换离开它的当前节点;当一个转换名称在信号中被指定时,令牌会经由指定的转换离开节点。发送到流程实例的信号被委托给根令牌。
令牌进入节点后,节点被执行。节点自己有责任让图继续执行,图的继续执行是通过让令牌离开节点完成的。每个节点类型为了图的继续执行可以实现不同的行为,如果一个节点不能传播图的执行,则被表现为一个状态。
动作(Action)是在流程执行中的事件上被执行的java代码片断。在软件需求中,图是信息交流的一个重要手段,但是图只是将要生产的软件的一个视图(影像),它隐藏了很多技术细节。动作是在图的表示之外添加技术细节的一种机制,一旦图被做好,它可以由动作来修饰。主要的事件类型有:进入节点、离开节点、执行转换。
9.2 流程图
基本的流程定义是一个由节点和转换组成的图,这些信息在processdefinition.xml中表示。每个节点都有一个类型,如state、decision、fork、join等;每个节点有一组离开转换,可以给离开节点的每个转换一个名称来区分它们。例如:下图显示了jBay拍卖流程的流程图。

图 9.1拍卖流程图
下面是jBay拍卖流程图的xml表示:
<process-definition>
 
 <start-state>
    <transition to="auction" />
 </start-state>
 
 <state name="auction">
    <transition name="auction ends" to="salefork" />
    <transition name="cancel" to="end" />
 </state>
 
 <fork name="salefork">
    <transition name="shipping" to="send item" />
    <transition name="billing" to="receive money" />
 </fork>
 
 <state name="send item">
    <transition to="receive item" />
 </state>
 
 <state name="receive item">
    <transition to="salejoin" />
 </state>
 
 <state name="receive money">
    <transition to="send money" />
 </state>
 
 <state name="send money">
    <transition to="salejoin" />
 </state>
 
 <join name="salejoin">
    <transition to="end" />
 </join>
 
 <end-state name="end" />
 
</process-definition>
9.3 节点
流程图是由节点和转换组成的,有关图的以及它的扩展模型的更多信息,请参考“第4章 面向图的编程”TODO。
每个节点有一个特定类型,节点类型决定了在运行时执行到达节点时将发生什么。Jbpm有一组你可以使用的预定义的节点类型,另外,你也可以编写定制代码来实现你自己指定的节点行为。
9.3.1 节点责任
每个节点都有两个主要责任:首先,它可以执行普通java代码,典型情况下,java代码与节点功能是相关的,例如:创建一些任务实例、发送一个通知、更新一个数据库等;其次节点要负责传播流程执行。基本上来说,每个节点在传播流程执行时有以下几个可选方式:
1.不传播执行。这种情况下节点表现为一个等待状态。
2.经由节点的某个离开转换传播执行。这意味着到达本节点的令牌使用API调用executionContext.leaveNode()经由某个离开转换被传递,这时节点作为一个自动节点,它可以执行一些定制的程序逻辑然后自动继续流程执行,而没有等待。
3.创建一个新的执行路径。节点可以决定创建新的令牌,每个新的令牌表示一个新的执行路径,并且每个令牌可以通过节点的离开转换被启动。这种行为的一个很关好的例子就是fork节点。
4.结束执行路径。节点可以决定结束执行路径,这意味着令牌被结束,执行路径也就完结了。
5.更一般的情况,节点可以修改流程实例的整个运行时结构。运行时结构是包含一个令牌树的流程实例,每个令牌表示一个执行路径,节点可以创建和结束令牌,把每个令牌放在图中的节点,并且通过转换启动令牌。
 
Jbpm像其他任何工作流和BPM引擎一样,包含一组预实现的节点类型,它们有特定的文档配置和行为,但是关于Jbpm和面向图的编程基础(第4章 面向图的编程TODO)相对于其他来说其独特之处是对开发者开放模型,开发者可以很容易的编写自己的节点行为,并在流程中使用。
这也就是传统的工作流和BPM系统非常封闭之处,它们通常提供一组固定的节点类型(叫做流程语言),它们的流程语言是封闭的并且执行模型被隐藏在运行环境中。研究工作流模式可以发现,任何流程语言都不足够强大,我们决定做一个简单模型,并且允许开发者编写他们自己的节点类型,而JPDL流程语言则是完全开放的。
接下来我们论述JPDL中非常重要的节点类型。
9.3.2 节点类型task-node
任务节点代表一个或多个被人所执行的任务。因此当执行到达一个任务节点时,任务实例将会在工作流参与者的任务列表中被创建,然后,节点将表现为一个等待状态,当用户执行了他们的任务时,任务的完成会触发恢复执行,换句话说,这将导致一个新的信号在令牌上被调用。
9.3.3 节点类型state(状态)
状态就是一个等待状态,与任务节点不同的是没有任务实例在任务列表中被创建,如果流程需要等待一个外部系统,这是有用的。例如,在节点的入口处(通过node-enter时间的动作),可以发送一个消息到外部系统,然后流程进入等待状态,当外部系统发送一个响应消息时,这可以导致一个token.signal(),用来触发恢复流程执行。
9.3.4 节点类型decision(决策)
实际上有两种方式来建模决策,这两种方式之间的不同是基于“谁”来做决策,可以由流程(请阅读:在流程定义中指定)来做决策,或者由外部实体来提供决策结果。
当由流程来做决策时,就应该使用决策节点了。有两个基本的方法来指定决策标准:简单的方式是在转换上添加条件元素(condition),条件是返回一个布尔值的beanshell脚本表达式,在运行时,决策节点会循环它的离开转换(按照xml中指定的顺序)并计算每个条件,第一个条件结果返回为“true”的转换将被使用。另外一种可选方式是指定一个DecisionHandler的实现,然后决策在java类中被计算并且通过DecisionHandler实现的decide方法返回所选的离开转换。
当决策是由外部部分(意思是:不是流程定义的一部分)来决定时,你可以使用多个转换离开一个状态或等待状态节点,然后,离开转换可以被提供到外
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值