jbpm第三章翻译

第三章 指南

这篇指南将要介绍给你用jpdl构建基本的流程和怎样用jbpmapi来管理运行的流程。

这篇指南的格式是解释了一系列的例子,这些例子都关注一个特定的主题并包括相关的注释。这些例子也可以在jbpm下载包中的examples目录中找得到。

最好的学习方式是建立一个工程并对我们给定的例子作些修改试验着学习。

Eclipse用户可以这么开始我们的学习:下载jbpm-3.0-[version].zip并解压到你的系统。然后选中"File" --> "Import..." --> "Existing Project into Workspace"。点击"Next",然后浏览到jbpm的根目录并点击"Finish"。现在在你的工作区中你已经有了一个jbpm工程了。你现在可以在src/java.examples/....找到这些例子,当你打开这些例子的时候,你可以运行它们通过选择. "Run" --> "Run As..." --> "JUnit Test"

Jbpm拥有一个图形化的定制器来完成定制例子中的xml文件。你可以在第二章第一节“可下载文件概况”中找到图形化定制器的下载说明。

3.1 Hello World例子

一个流程定义是一个由节点(node)和转换(transition)组成的有向图。hello world流程有三个节点。为了看这些组成部分是如何组合的,我们以一个没有用设计器设计的简单的流程开始。下边这幅图就是hello world流程的图形表示:

3.1 Hello World 流程图

public void testHelloWorldProcess() {

  //这个方法演示了一个流程的定义和这个流程定义的具体执行。

  //这个流程定义包含三个节点:一个未命名的开始状态(start-state),

  //一个名字为's'的状态(state)和一个名字为'end'的结束状态(end-state)。

  //下一行的功能是把一段xml文本解析为一个ProcessDefinition,一个//ProcessDefinition是一个java对象的形式对流程的正式的描述。

  ProcessDefinition processDefinition = JpdlXmlReader.parse(

    "<process-definition>" +

    "  <start-state>" +

    "    <transition to='s' />" +

    "  </start-state>" +

    "  <state name='s'>" +

    "    <transition to='end' />" +

    "  </state>" +

    "  <end-state name='end' />" +

    "</process-definition>"

  );

 

  //下边的一行根据流程定义构造了的一个具体的执行实例。

  //构造以后,执行的流程就有了一个被定位在开始状态(start-state上的主要的执行路径

//root token

 

  ProcessInstance processInstance =

      new ProcessInstance(processDefinition);

 

  //构造以后,执行的流程就有了一个主要的执行路径(root token

 

  Token token = processInstance.getRootToken();

 

  //当然,构造以后,流程定义的主要的执行路径被定位在开始状态(start-state

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

 

  //开始流程执行,通过默认的转换(transition)离开开始状态(start-state

  token.signal();

  //直到运行的流程进入一个等待状态,signal方法将一直被阻塞

  //运行的流程将要进入第一个等待状态:状态‘s.因此现在主要的执行路径

 //定位到了状态‘s’上。

 

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

 

  //让我们释放另外的一个signal,流程将继续执行,将通过默认的转换(transition)离开状态‘s

  token.signal();

  //现在signal方法将得到返回,因为流程实例已经到达了结束状态。

 

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

}

3.2 数据库例子

Jbpm的一个基本特性就是假如运行的流程处于等待状态,jbpm可以把它们保存到数据库中。下一个例子演示了如何保存流程实例到jbpm的数据库中。这个例子还给出了一个可能发生的处理上下文(context)的建议。比如,web应用程序中用户代码的一部分开启了一个流程并且把运行实例保存到数据库中。后来,一个消息驱动bean从数据库中加载了这个流程实例并且继续执行这个流程。

Jbpm持久化的更多信息请参照第六章 持久化

 

 

public class HelloWorldDbTest extends TestCase {

//在每个应用中我们都需要一个JbpmSessionFactory因此我们把它放在一个//静态变量中。在我们的测试方法中,JbpmSessionFactory被用来产生JbpmSession's

  

  static JbpmSessionFactory jbpmSessionFactory = 

      JbpmSessionFactory.buildJbpmSessionFactory();

  

  static {

    //既然我们用的hypersonic数据库是在内存中的,是一个新的,空的数据//库,所以我们必须在我们开始测试之前实时产生数据库schema下一行代码//创建了数据库的表并设置了外键约束。

    jbpmSessionFactory.getJbpmSchema().createSchema();

  }

 

  public void testSimplePersistence() {

//在下边调用的三个方法之间,所有的数据都是通过数据库传输的,这里,//在这个单元测试中,这三个方法依次被调用因为我们想测试一个完整的流程。//但是在实际的应用中,这些方法代表对服务器不同的请求。

//既然我们从一个空的干净的内存数据库开始,我们必须部署从部署流程开

//始。在现实中,这只被流程开发人员部署一次。

    deployProcessDefinition();

//假象当在web应用程序中一个用户提交了一个表单,我们想开启一个流程实//

    

    processInstanceIsCreatedWhenUserSubmitsWebappForm();

 

    //然后,一个异步消息的到达将会使流程继续执行

    theProcessInstanceContinuesWhenAnAsyncMessageIsReceived();

  }

 

  public void deployProcessDefinition() {

    //这个测试程序演示了一个流程定义和这个流程定义的运行。这个流程定义//包括三个节点:一个未命名的开始状态,一个名字为‘s’的状态,和一个名//字为’end’的结束状态。

    ProcessDefinition processDefinition = JpdlXmlReader.parse(

      "<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>

    );

    

    //开启一个新的持久化session

    JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();

    // ... 并且在这个持久化session上开启一个事务

    jbpmSession.beginTransaction();

    

    // 保存流程定义到数据库中 

    jbpmSession

        .getGraphSession()

        .saveProcessDefinition(processDefinition);

    

    // 提交事务

    jbpmSession.commitTransaction();

    // 关闭jbpmSession

    jbpmSession.close();

  }

 

  public void processInstanceIsCreatedWhenUserSubmitsWebappForm() {

    //这个方法中的代码应该在一个struts-action,或在一个jsf的管理//bean中(JSF managed bean)。

    

    // 开启一个新的持久化session

    JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();

    // ... 并且在这个持久化session上开启一个事务

    jbpmSession.beginTransaction();

    

    //现在我们可以在数据库中查找我们在上边部署的流程定义

    ProcessDefinition processDefinition = 

        jbpmSession

          .getGraphSession()

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

    //现在流程实例被保存到数据库中。所以流程实例的当今状态也被保存到了//数据库

    

    

    jbpmSession

        .getGraphSession()

        .saveProcessInstance(processInstance);

//下边的方法从数据库中取回流程实例并且通过提供另外一个外部信号

//signal)是流程继续执行。

 

  //在这个web应用程序最后的action中,事务被提交    

    jbpmSession.commitTransaction();

    //关闭jbpmSession

    jbpmSession.close();

  }

 

  public void theProcessInstanceContinuesWhenAnAsyncMessageIsReceived() {

    //这个方法中的代码应该在一个消息驱动bean    

    // 开启一个新的持久化session

    JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();

    // ... 并且在这个持久化session上开启一个事务

    //请注意在你的应用服务器中,通过jdbc连接数据源也是可以的

    jbpmSession.beginTransaction();

    GraphSession graphSession = jbpmSession.getGraphSession();

 

    //

    ProcessDefinition processDefinition = 

        graphSession.findLatestProcessDefinition("hello world");

 

    // 现在我们在所有的流程实例中查找这个流程实例

    List processInstances = 

        graphSession.findProcessInstances(processDefinition.getId());

    //我们知道在这个单元测试的上下文环境中只能有一个运行的实例。在现实//开发中,流程实例的id号可以从到达的信息的内容中或从用户的选择中提取//出来

    

    ProcessInstance processInstance = 

        (ProcessInstance) processInstances.get(0);

    

    //现在可以继续执行这个流程实例。请注意是这个流程实例向执行主路径(root token)发出信号的。

    processInstance.signal();

 

    //这个signal过后,我们知道流程实例应该已经到达结束状态了

    assertTrue(processInstance.hasEnded());

    

//现在我们可以更新数据库中的流程实例的状态

    graphSession.saveProcessInstance(processInstance);

 

    // 在这个消息驱动bean的最后,事物被提交

    jbpmSession.commitTransaction();

    //关闭jbpmSession

    jbpmSession.close();

  }

}

3.3 上下文例子:流程变量

流程变量包含流程执行时期的上下文信息。流程变量和java.util.Map非常相像,也是变量名(name)到变量值(value)的对应关系,就像java.util.Map中对应的是java object

流程变量被当成流程实例的一部分保存。我们为了使事情简单,在这个例子中,我们仅仅演示一下和流程变量相关的api,并没有持久化。

关于流程变量的更多信息请参照第八章 上下文

//这个例子依旧从hello world流程开始,这次甚至都没有修改

ProcessDefinition processDefinition = JpdlXmlReader.parse(

  "<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访问,也可以在动作(action和节点的执行时访问。流程变量被作为流程实例的一部分保存在数据库中。

 

processInstance.signal();

 

// 变量通过上下文环境实例(contextInstance)来访问 

 

assertEquals(new Integer(500), 

             contextInstance.getVariable("amount"));

assertEquals("i met my deadline", 

             contextInstance.getVariable("reason"));

3.4 任务分配例子

在下一个例子中,我们将向你演示怎样分配给用户一个任务。因为jbpm工作流引擎和组织模型是分离的,用表述语言推算参与者(actor)限制都蛮多。因此,你必须去做AssignmentHandler接口的一个具体实现来推算参与者actor的任务(task)。

 

public void testTaskAssignment() {

  //下边的流程是基于hello world流程的。状态节点被替换为任务节点。任务节点在jpdl中代表一个等待状态,并且它能产生待完成的任务,这些任务必须被完成流程才能继续执行。

  ProcessDefinition processDefinition = JpdlXmlReader.parse(

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

  //开始执行流程,通过默认的转换(transition)离开开始状态(start-state

token.signal();

// signal方法将一直被阻塞,一直到执行的流程进入一个等待状态(wait //state),在这//里,它是个任务节点(task-node

  

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

//当执行的流程到达任务节点的时候,一个“换尿布”的任务被创建,并且//NappyAssignmentHandler将被调用,来决定这个任务应该分给谁。这里//NappyAssignmentHandler返回的是“爸爸”。

 

//在实际情况中,任务应该是通过org.jbpm.db.TaskMgmtSession中的方法从//数据库中获取出来的。既然我们这里并不想把复杂的持久化引入这里例子,//我们仅仅获取流程实例中的第一个任务实例(我们知道在这个测试当中只有//一个任务)

  

  TaskInstance taskInstance = (TaskInstance)  

      processInstance

        .getTaskMgmtInstance()

        .getTaskInstances()

        .iterator().next();

 

 //现在我们来检查一下流程实例是不是真正的分给了“爸爸”

  assertEquals("papa", taskInstance.getActorId() );

  

  //现在我们假定“爸爸”已经完成了他的任务,并且把任务标记为已完成。

  taskInstance.end();

  //既然这是最后(唯一)一个要完成的任务,这个任务的完成将触发流程实例//的继续执行。

  

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

}

3.5 自定义action例子

Action是把你的java代码集成到jbpm流程中的一套机制。Action可以和它自己的节点(node)关联(如果它们在流程图中是相关的)。Action也可以被放置在以下事件中,比如:转换(transition)发生,离开节点或进入一个节点。这种情况下,action并不是流程图表示的一部分,但是在流程的执行过程中,当执行激活这些事件时,action也会被执行。

我们看一下在我们例子中将要用的action的实现:MyActionHandler。这个action处理动作的实现并没有做很复杂的事情……,它仅仅是把boolean变量isExecuted设置为trueisExecuted变量是静态的,这样就可以像在action外部检验它的值一样在action处理动作的内部访问它。

关于action的更多资料请参照第七章的第四节 “Actions”

// MyActionHandler展示了一个在jbpm流程执行的时候可以执行一些用户代码的类

public class MyActionHandler implements ActionHandler {

//在每个测试的开始(在setup方法中),成员isExecuted都被设置成false  

  public static boolean isExecuted = false;  

//action将要把isExecuted设置为true,这样当action被执行的时候就可以//在这个单元测试中显现出来

  

   public void execute(ExecutionContext executionContext) {

    isExecuted = true;

  }

}

就像我们在前边提到的,在每个测试的开始,我们都要把静态变量MyActionHandler.isExecuted设置为false

//每个测试开始之前,都把MyActionHandler的成员静态变量isExecuted设置为false

  public void setUp() {

MyActionHandler.isExecuted = false;

}

我们在一个转换(transition)上开始我们的action

public void testTransitionAction() {

    //下边的流程是hello world流程的一个变种。我们在由状态’s’向结束状态转换上放了一个action。这个测试的目的是想展示把jbpm流程和java代码集成起来是多么的容易。

    ProcessDefinition processDefinition = JpdlXmlReader.parse(

      "<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将使执行的流程实例离开开始状态并进入状态’s’

    processInstance.signal();

 

    // 我们这里是为了展示MyActionHandler没有被执行

    assertFalse(MyActionHandler.isExecuted);

    //……并且执行的主路径被定位在状态’s’

        assertSame(processDefinition.getNode("s"), 

               processInstance.getRootToken().getNode());

    //下一个signal方法将激活root token的执行。这个token将要带着这个//action发生转换(transition)并且这个actionsignal方法被调用的时//候会被执行。

    

    processInstance.signal();

    //这里我们可以看到当调用signal方法的时候,MyActionHandler被执行

    

    assertTrue(MyActionHandler.isExecuted);

  }

下一个例子演示了相同的action,但是现在action被分别放置在enter-node leave-node事件上。注意一个节点(node)和转换(transition)相比拥有多于一个的事件类型,转换(transition)只拥有一个事件。因此放置在node上的action因该被放置在一个事件元素上。

ProcessDefinition processDefinition = JpdlXmlReader.parse(

  "<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将会使流程执行,离开开始状态进入名字为’s’的状态。这//’s’状态被进入因此action被执行。

processInstance.signal();

assertTrue(MyActionHandler.isExecuted);

 

// 重新设置MyActionHandler.isExecutedfalse 

MyActionHandler.isExecuted =;

//下一个signal将要触发流程的执行,来离开状态’s’。这样action将会被//再执行一次

processInstance.signal();

// 结果应该如我们所预料的一样。  

assertTrue(MyActionHandler.isExecuted);

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值