jBPM jPDL 3.2用户指南:第3章教程

作者:JBossWeek http://blog.csdn.net/JBossweek email:jbossweek AT gmail.com

版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息

     

这个教程将展示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。图形化设计器的下载说明请参看Section 2.1, "Downloadables Overview"。该教程不需要使用流程设计器工具。

3.1. Hello World 示例

流程定义是一个有向图,由节点和转换组成。Hello world流程有三个节点。为了理解这些如何组合在一起,我们将从一个简单的流程开始,并且不使用流程设计器。下面的图给出了hello world流程的图形化表示:

Figure 3.1.  hello world 流程图

 

public void testHelloWorldProcess() {

 

  // 该方法展示了流程定义和它的一次执行。该流程定义有3个节点:

 

  // 没有名称的start-state,一个状态's'和名为'end'的end-state。  

 

  // 下面的代码行将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);

 

  //创建以后,流程实例有一个主执行路径(=the root token)。
			

 

  Token token = processInstance.getRootToken();

 

  //主执行路径停留在流程定义的start-state上
			

 

  assertSame(processDefinition.getStartState(), token.getNode());

 

  // 让我们开始执行,执行将通过缺省转换离开start-state
			

 

  token.signal();

 

  // signal方法直到流程执行进入一个等待状态才会阻塞
			

 

  // 流程执行已经进入第一个等待状态's'。因此现在主执行路径停留在状态's'
			

 

  assertSame(processDefinition.getNode("s"), token.getNode());

 

  // 让我们再发送一个signal,流程将继续执行,通过缺省转换离开状态's'
			

 

  token.signal(); 

 

  // 现在signal方法将会返回,因为流程实例已经进入end-state
			

 

  assertSame(processDefinition.getNode("end"), token.getNode());

 

}

3.2. 数据库示例

在等待状态能够将流程执行持久保存到数据库是jBPM的一个基本特性。下面的例子将演示如何将流程实例保存到Jbpm数据库。这个例子也给出了可能需要使用持久化特性的环境。不同的用户代码片段都单独创建了方法,例如web应用中的代码片段用于启动流程并将执行持久化保存到数据库,然后消息驱动bean的用户代码片段从数据库加载流程实例继续执行。

关于jBPM持久化更详细的信息请参看Chapter 7, Persistence

 

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引用环境的服务分离
			

 

      "  <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应用中当用户提交form时启动一个流程实例(=流程执行)
			

 

processInstanceIsCreatedWhenUserSubmitsWebappForm();

 

// 然后,当接收到异步消息时执行将继续
			

 

    theProcessInstanceContinuesWhenAnAsyncMessageIsReceived();

 

  }

 

  public void deployProcessDefinition() {

 

// 这个测试展示流程定义和它的一个执行。流程定义有三个节点:

 

// 没有名称的start-state,状态's'和名为'end'的end-state。
			

 

    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 persistence context生成器
			

 

    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

 

    try {

 

    // 将流程定义部署到数据库
			

 

      jbpmContext.deployProcessDefinition(processDefinition);

 

    } finally {

 

      // 关闭pojo persistence context,包括将插入的流程定义sql语句flush到数据库
			

 

      jbpmContext.close();

 

    }

 

  }

 

  public void processInstanceIsCreatedWhenUserSubmitsWebappForm() {

 

// 这个方法中的代码可能放在struts-action或者JSF managed bean中
			

 

// 查找上面配置的pojo persistence context生成器
			

 

    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

 

    try { 

 

      GraphSession graphSession = jbpmContext.getGraphSession();

 

      ProcessDefinition processDefinition = 

 

          graphSession.findLatestProcessDefinition("hello world");

 

      // 通过从数据库获取processDefinition,

 

      // 我们可以像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);

 

      // 下面的方法将从数据库中取回流程实例,并发送另外一个外部的signal让流程继续执行
			

 

} finally {

 

      // 关闭pojo persistence context
			

 

            

 

      jbpmContext.close();

 

    }

 

  }

 

  public void theProcessInstanceContinuesWhenAnAsyncMessageIsReceived() {

 

// 这个方法里的代码可以是消息驱动bean的内容
			

 

//查找上面配置的pojo persistence context生成器
			

 

    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();

 

try { 

 

      GraphSession graphSession = jbpmContext.getGraphSession();

 

      // 首先,需要从数据库取回流程实例。获得当前操作流程实例的方法有几种。

 

      // 在简单的test case中最容易的方法就是查询流程实例的整个列表。

 

      // 它应该只有一个返回值。因此让我们从查找流程定义开始
			

 

      ProcessDefinition processDefinition = 

 

          graphSession.findLatestProcessDefinition("hello world");

 

      // 现在,查找这个流程定义的所有流程实例
			

 

      List processInstances = 

 

          graphSession.findProcessInstances(processDefinition.getId());

 

      // 我们知道在单元测试环境中,只有一个执行。

 

      // 在实际应用中,processInstanceId可以从到达的消息内容中获取,

 

      // 也可以通过用户选择得到
			

 

      ProcessInstance processInstance = 

 

          (ProcessInstance) processInstances.get(0);

 

      // 现在,我们可以继续执行。注意,

 

      // processInstance将signal委托给执行的主路径(=the root token)
			

 

      processInstance.signal();

 

      // 发送signal以后,我们知道流程执行应该进入end-state
			

 

      assertTrue(processInstance.hasEnded());

 

      // 现在,可以更新数据库中执行的状态
			

 

      jbpmContext.save(processInstance);

 

} finally {

 

      // 关闭pojo persistence context
			

 

            

 

      jbpmContext.close();

 

    }

 

  }

 

}

3.3. Context 示例: 流程变量

在流程的执行过程中,流程变量包含流程的上下文信息。流程变量与java.utilMap类似,java.utilMap将名称映射成值,流程变量则将名称映射成java对象。流程变量将作为流程实例的一部分进行持久化。为了简单,这个例子没有实用持久化,只展示了操作流程变量 API的使用,。

关于流程变量的更多信息,请参看Chapter 10, Context

 

// 这个例子也从hello world流程开始,没有对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);

 

// 从流程实例获取用于操作流程变量的context instance
			

 

ContextInstance contextInstance = 

 

  processInstance.getContextInstance();

 

// 在流程离开start-state之前,在流程实例的context设置一些流程变量
			

 

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工作流引擎与组织机构模型之间是分离的,采用表达式语言来计算任务actor的局限性太大。因此,需要通过指定AssignmentHandler的实现来计算任务的actor。

 

public void testTaskAssignment() {

 

  // 下面演示的流程基于hello world流程,只是将状态节点替换成任务节点。

 

  // 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();

 

  // 让我们开始执行流程,执行通过缺省转换离开start-state
			

 

  token.signal();

 

  // signal方法将会阻塞直到流程执行进入等待状态。这个例子里是任务节点。
			

 

  assertSame(processDefinition.getNode("t"), token.getNode());

 

  // 当执行进入任务节点,将会创建'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. 定制action 示例

Action是将java定制代码绑定到jBPM流程的一种机制。Action可以与自身的节点关联(如果它们在流程的图形化表示中相关联),也可以与事件关联,例如转移、离开节点或者进入节点。在这些情况中,Action虽然不是图形化表示的一部分,但是在运行流程执行过程中激发该事件时它们仍将被执行。

我们将从例子中使用的action实现MyActionHandler开始。该action handler实现没有完成任何具体的工作,它仅仅将boolean变量isExecuted设置为true。isExecuted是静态变量,因而既可以在该action handler中访问,也可以在确认isExecuted值的action中访问。

关于Action更多的信息,请参看Section 9.5, "Actions"

 

// MyActionHandler表示在Jbpm流程执行过程中可能执行一些用户代码的类
			

 

public class MyActionHandler implements ActionHandler {

 

  // 在每次测试之前(setUp中),成员变量isExecuted被设置为false
			

 

  public static boolean isExecuted = false;  

 

  // 这个action将成员变量isExecuted设置为true,
			

 

  public void execute(ExecutionContext executionContext) {

 

    isExecuted = true;

 

  }

 

}

前面已经提到,在每次测试之前,我们将静态变量MyActionHandler.isExecuted的值设置为false。

 

  // 每次测试都从将静态成员变量isExecuted值设置为false开始
			

 

  public void setUp() {

 

    MyActionHandler.isExecuted = false;

 

  }

我们将从一个transition上的action开始。

 

public void testTransitionAction() {

 

// 下面的流程是hello world流程的变体,

 

// 在从状态's'到end-state的transition中增加了一个action。

 

// 这个测试的目的是演示将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); 

 

// 下面的signal将引起执行离开start state进入状态's'
			

 

processInstance.signal();

 

// 这里我们确认MyActionHandler还没有执行
			

 

assertFalse(MyActionHandler.isExecuted);

 

// 并且执行的主路径停留在状态's'
			

 

    assertSame(processDefinition.getNode("s"), 

 

               processInstance.getRootToken().getNode());

 

// 紧接着的signal调用将触发root token执行。

 

// Token经过transition,其中的action将被执行

 

processInstance.signal();

 

// 这里将会看到在调用signal方法的过程中MyActionHandler已经被执行
			

 

    assertTrue(MyActionHandler.isExecuted);

 

  }

下面的例子将展示同一个action,但是现在将action分别放在enter-node和 leave-node的事件上。注意与transition只有一个事件不同,节点拥有不止一个类型的事件。因此只能将action设置到节点的事件元素上。

 

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);

 

// 下面的signal将引起执行离开start state进入状态's'。

 

// 进入状态's'后,action将被执行
			

 

processInstance.signal();

 

assertTrue(MyActionHandler.isExecuted);

 

// 让我们重置MyActionHandler.isExecute
			

 

MyActionHandler.isExecuted = false;

 

// 下面的signal将触发执行离开状态's'。因此流程将会继续执行
			

 

processInstance.signal();

assertTrue(MyActionHandler.isExecuted);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值