分享

Hello World 例子---jbpm(摘抄)

 luodingzh 2011-08-11

第三章. 讲座

这个讲座将展示如何建造一个jpdl和如何使用API的方法来管理运行时的执行.

这个讲座的方式是解释一系列的例子. 每个例子将集中关注特别的主题和额外的说明. 这些例子可以在jBPM包的examples目录下找到.

最好的方法就是学着来建立一个Project实验所给的例子.

eclipse 用户注意:下载jbpm-3.0-[version].zip并且解压缩到系统. 然后从菜单 "File" --> "Import..." --> "Existing Project into Workspace". 点 "Next" 然后浏览jBPM 根目录然后点击 "Finish". 现在你的有了jbpm 3 project了. 你可以发现这个讲座目录在目录 src/java.examples/... . 你打开这些例子后,可以运行它们"Run" --> "Run As..." --> "JUnit Test"

jBPM 包括一个图形设计器来产生例子中的XML. 你可以从这里下载和学习有关图形设计器的说明  节 2.1, “下载一览”

3.1. Hello World 例子

一个流程 是有一个定向图(directed graph)来定义,由节点和变换组成 . hello world 流程有3个节点.如下看如何组合起来, 我们先开始一个简单的流程不使用图形设计器. 下面的图形表示hello world 流程:

The hello world process graph

Figure 3.1.  hello world 流程图

public void testHelloWorldProcess() {
        // 这个方法展现流程定义和流程执行的定义
        // 流程定义有3个节点. 
        // 一个未命名的开始状态start-state,一个状态 's' 
  // 一个结束状态名字为'end'.
         // 下面行解析一个xml text为一个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>"
);
         // 下一行建立了一个流程执行定义.
         //在构造后,流程执行有一个主要的执行路径
  // (=    root token 根令牌) 此时位置在start-state处
ProcessInstance processInstance =
new ProcessInstance(processDefinition);
        // 在构造后流程执行有一个主要的执行路径
// (=   roottoken 根令牌) .
Token token = processInstance.getRootToken();
        // 构造后, 位置处于流程定义执行路径start-state的位置
assertSame(processDefinition.getStartState(), token.getNode());
      // 现在开始流程执行,离开start-state 结束默认的转换(transition) 
token.signal();
      // 这个signal方法将会阻塞直到流程执行进入 wait 状态 
       // 流程执行在状态's' 进入第一个 等待状态
         // 因此执行主路径现在位置在 状态's' 
assertSame(processDefinition.getNode("s"), token.getNode());
       // 我们再送另外一个信号signal. 这将继续执行离开状态's' 结束默认的转换(transition) 
token.signal();
       // 现在信号signal方法将返回,因为流程实例到达了end-state 结束状态 
assertSame(processDefinition.getNode("end"), token.getNode());
}

3.2. 数据库例子

jBPM一个基本的特性是当流程处于等待状态时候可以把流程执行 永久化到数据库中 . 下一个例子想你展示了如何存储一个流程实例到jBPM数据库中. 例子给出一个将会发生的上下文.分开的方法用来建立不同部分的用户代码. 比如一部分用户代码在web 应用程序中开始一个流程并永久化执行到数据库中.然后,message drive bean从数据库中载入流程实例并继续它的执行

jBPM 永久化的更多内容可以参看  第六章, 永久化.

public class HelloWorldDbTest extends TestCase {
          // 我们在每个应用程序中需要一个JbpmSessionFactory. 因此我们把它放在一个静态变量中 
       // JbpmSessionFactory 在test 方法中来建立一个 
     JbpmSession's.
static JbpmSessionFactory jbpmSessionFactory =
JbpmSessionFactory.buildJbpmSessionFactory();
static {
         // 因为HSQLDBin-memory数据库是干净的数据库,
            // 在我们开始测试前,我们需要建立table.
// The next line creates the database tables and foreign key 
// constraints. 
jbpmSessionFactory.getJbpmSchema().createSchema();
}
public void testSimplePersistence() {
           // 在3个方法调用下面方法中间,所有数据被写入数据库 
           // 在单元测试中,这3个方法被正确执行在每个方法之后
           // 因为我们想完成测试流程场景
        // 但在实际中这些方法代表着server不同的请求 
           // 因为我们开始的数据库是个干净的数据库,我们需要首先发布流程在里面 
            // 在真实中,这个是由流程开发人员完成的 
deployProcessDefinition();
        // 假定我们想开始流程实例(= 流程执行)
         // 当用户提交一个Web表单的时候.
processInstanceIsCreatedWhenUserSubmitsWebappForm();
        // 然后,到达的异步消息将继续执行 
theProcessInstanceContinuesWhenAnAsyncMessageIsReceived();
}
public void deployProcessDefinition() {
//定义一个流程,包括三个及点,一个未命名的start-state,一个状态's'
//一个结束状态 end-state名字'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>
);
     // 打开新的永久层会话
JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();
        // 并且在永久层会话上开启事务
jbpmSession.beginTransaction();
      // 保存流程定义到数据库中 
jbpmSession
.getGraphSession()
.saveProcessDefinition(processDefinition);
  // 提交事务
jbpmSession.commitTransaction();
   // 关闭会话.
jbpmSession.close();
}
public void processInstanceIsCreatedWhenUserSubmitsWebappForm() {
       // 这个方法里的代码可以放在structs action或JSF管理bean 里 
     // 打开一个新的永久层会话
JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();
        // 启动事务.
jbpmSession.beginTransaction();
           // 查询数据库得到我们在上面步骤发布的流程定义 
ProcessDefinition processDefinition =
jbpmSession
.getGraphSession()
.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());
          // 流程实例被保存在数据库 
           // 所以当前流程执行的状态被保存进数据库 
 .  
jbpmSession
.getGraphSession()
.saveProcessInstance(processInstance);
// The method below will get the process instance back out 
// of the database and resume execution by providing another 
// external signal.
          // web应用程序动作结束出,事务被提交.
jbpmSession.commitTransaction();
   // 关闭jbpmSession.
jbpmSession.close();
}
public void theProcessInstanceContinuesWhenAnAsyncMessageIsReceived() {
           // 这个代码可以包含在message driven bean中.
     // 打开新的永久性的会话.
JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();
        // 永久化会话上开始事务
           // 说明它也可能使用应用服务器的DataSource的JDBC连接 
jbpmSession.beginTransaction();
GraphSession graphSession = jbpmSession.getGraphSession();
// First, we need to get the process instance back out of the database.
// There are several options to know what process instance we are dealing 
// with here.  The easiest in this simple test case is just to look for 
// the full list of process instances.  That should give us only one 
// result.  So let's look up the process definition.
ProcessDefinition processDefinition =
graphSession.findLatestProcessDefinition("hello world");
          // 现在,我们搜索这个流程定义的所有流程实例.
List processInstances =
graphSession.findProcessInstances(processDefinition.getId());
// We know that in the context of this unit test there is 
// only one execution.  In real life, the processInstanceId can be 
// extracted from the content of the message that arrived or from 
// the user making a choice.
ProcessInstance processInstance =
(ProcessInstance) processInstances.get(0);
// 我们可以继续执行. 说明流程实例委托信号到执行主路径(=         the root token)
processInstance.signal();
       // 在singal后, 我们知道流程执行应该到 end-state 
assertTrue(processInstance.hasEnded());
           // 现在我们可以更新执行状态到数据库中
graphSession.saveProcessInstance(processInstance);
        // MDB结束, 事务被提交.
jbpmSession.commitTransaction();
    // jbpmSession被关闭.
jbpmSession.close();
}
}

3.3. 上下文例子: 流程变量

在流程执行时候流程变量包含上下文信息. 流程变量同java.util.Map相似映射名字到值,值可能是个java对象 . 流程变量被永久化作为流程实例的一部分. 为了让事情简单,这个例子中我们仅仅展示使用变量的API而没有永久化.

关于变量的更多信息可以参看 第8章 上下文

      // 这个例子也是从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();
      // 在开始之前流程离开了start-state, 
         // 我们准备设置一些流程变量在流程实例上下文中 
     .
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工作流引擎和组织模型是分开的,一种用来计算参与者表达语言总是受限的. 因此,你不得不指定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();
       // 开始流程执行,完整默认的转换后离开start-state 
    .
token.signal();
       // signal 方法将被阻塞知道流程执行进入等待状态. 
            // 在这个case中是指task-node.
assertSame(processDefinition.getNode("t"), token.getNode());
       // 当执行到达task-node, 任务'change nappy'
// 被建立并且NappyAssignmentHandler 被调用来决定任务将分派给睡
         //NappyAssignmentHandler 返回'papa' 
         // 在真实环境中, 任务将会从数据库中获取,通过or
       g.jbpm.db.TaskMgmtSession.
         // 因此这个例子中我们不想包括复杂的永久化 
         // 我们只是得到这个流程实例的第一个task-实例 
   (we know there is only one in this test
// 我们知道在这个测试场景中这里只有一个).
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是绑定你自己的定制java代码和jBPM流程的一种机制. 动作可以同它自己的节点关联起来 (如果它们在流程图表示中相关). 动作也可以放在事件event上比如. taking a transition, leaving a node 或者 entering a node.在这个case ,动作不是图表的一部分,但是它们在流程执行产生事件的时候,也会被执行.

我们将用一个例子: MyActionHandler 来观察动作的实现.这个动作handler实现不是什么非常特别的事情.它只是设置boolean变量 isExecuted 为 true . 变量 isExecuted 是静态的因此它可以在action handler内部被访问.

关于动作action的内容可以参看  7.4节, “动作”

     // MyActionHandler 就是一个class可以在jBPM流程执行时候在某些用户代码里被执行 
public class MyActionHandler implements ActionHandler {
      // 在测试之前, isExecuted 被设置为 
     false.
public static boolean isExecuted = false;
       // 动作将设置true 因此 当动作被执行 
       // unit test 将会展示
public void execute(ExecutionContext executionContext) {
isExecuted = true;
}
}
       // 每次测试开始都要设置MyActionHandler 的成员static isExecuted 为 
     false.
public void setUp() {
MyActionHandler.isExecuted = false;
}

我们将要在转换时开始一个动作

public void testTransitionAction() {
// The next process is a variant of the hello world process.
// We have added an action on the transition from state 's' 
// to the end-state.  The purpose of this test is to show 
// how easy it is to integrate java code in a jBPM process.
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>"
);
// Let's start a new execution for the process definition.
ProcessInstance processInstance =
new ProcessInstance(processDefinition);
// The next signal will cause the execution to leave the start 
// state and enter the state 's'
processInstance.signal();
       // 这里将显示 MyActionHandler还没有被执行 
assertFalse(MyActionHandler.isExecuted);
// ... and that the main path of execution is positioned in 
// the state 's'
assertSame(processDefinition.getNode("s"),
processInstance.getRootToken().getNode());
// The next signal will trigger the execution of the root 
// token.  The token will take the transition with the
// action and the action will be executed during the  
// call to the signal method.
processInstance.signal();
// Here we can see that MyActionHandler was executed during 
// the call to the signal method.
assertTrue(MyActionHandler.isExecuted);
}

下一个例子是同样的动作,但动作被分别放在 enter-node和 leave-node 事件 .注意节点同转换相比有超过一个事件类型(event type)转换(transition)只有一个事件.

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);
// The next signal will cause the execution to leave the start 
// state and enter the state 's'.  So the state 's' is entered 
// and hence the action is executed. 
processInstance.signal();
assertTrue(MyActionHandler.isExecuted);
// Let's reset the MyActionHandler.isExecuted  
MyActionHandler.isExecuted = false;
// The next signal will trigger execution to leave the  
// state 's'.  So the action will be executed again. 
processInstance.signal();
// Voila.  
assertTrue(MyActionHandler.isExecuted);

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多