2

BPMN 2.0介绍

定义流程

创建一个新的XML文件并为其命名,确保文件以.bpmn20.xml.bpmn结尾,否则引擎将不会选择该文件进行部署。

BPMN 2.0模式的根元素是definitions元素,在此元素内,可以定义多个流程定义(尽管建议在每个文件中只有一个流程定义,因为这样可以简化开发流程的后期维护)。空的流程定义如下所示,请注意,最少definitions元素仅需要xmlnstargetNamespace声明,targetNamespace可以是任何东西,对于对流程定义进行分类很有用。

<definitions
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:activiti="http://activiti.org/bpmn"
  targetNamespace="Examples">

  <process id="myProcess" name="My First Process">
    ..
  </process>

</definitions>

process元素具有两个属性:

  • id:此属性是必需的,并且映射到Activiti ProcessDefinition对象的key属性,然后,可以通过RuntimeService上的startProcessInstanceByKey方法,使用此id来启动流程定义的新流程实例,此方法将始终采用流程定义的最新部署版本。
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");
  • 这里要注意的重要一点是,这与调用startProcessInstanceById方法不同,此方法期望Activiti引擎在部署时生成的字符串id,可以通过调用processDefinition.getId()方法进行检索。生成的id的格式为key:version,并且长度限制为64个字符。如果你收到一个ActivitiException声明生成的ID太长,请限制该流程的键字段中的文本。
  • name:此属性是可选的,并且映射到ProcessDefinitionname属性,引擎本身不使用此属性,因此,例如,它可用于在用户界面中显示更人性化的名称。

入门:10分钟的教程

在本节中,我们将介绍一个(非常简单的)业务流程,我们将使用它介绍一些基本的Activiti概念和Activiti API。

前提条件

本教程假定你正在运行Activiti演示安装程序,并且你正在使用独立的H2服务器,编辑db.properties并设置jdbc.url=jdbc:h2:tcp://localhost/activiti,然后根据H2的文档运行独立服务器。

目标

本教程的目的是学习Activiti和一些基本的BPMN 2.0概念,最终结果将是一个简单的Java SE程序,该程序将部署流程定义,并通过Activiti引擎API与该流程进行交互。还将介绍Activiti周围的一些工具,当然,在围绕业务流程构建自己的Web应用程序时,也可以使用本教程中学习的内容。

用例

用例很简单:我们有一家公司,我们称之为BPMCorp。在BPMCorp中,每月需要为公司股东编写财务报告,这是会计部门的责任,报告完成后,高层管理人员之一需要批准该文件,然后再将其发送给所有股东。

流程图

可以使用Activiti Designer以图形方式显示上述业务流程,但是,在本教程中,我们将自己键入XML,因为我们在这一点上学到的最多,我们的流程的图形化BPMN 2.0表示法如下所示:

financial.report.example.diagram.png

我们看到的是一个none Start Event(左侧的圆圈),后面是两个用户任务:“Write monthly financial report”和“Verify monthly financial report”,以none end event(右侧带有粗边框的圆圈)结尾。

XML表示

该业务流程的XML版本(FinancialReportProcess.bpmn20.xml)如下所示:

  • (none) start event告诉我们该流程的切入点是什么。
  • 用户任务声明是我们流程中人工任务的表示,请注意,第一个任务分配给会计组,而第二个任务分配给管理组。
  • 当到达none end event时,该流程结束。
  • 元素通过顺序流相互连接,这些顺序流具有源和目标,它们定义了顺序流的方向。
<definitions id="definitions"
  targetNamespace="http://activiti.org/bpmn20"
  xmlns:activiti="http://activiti.org/bpmn"
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">

    <process id="financialReport" name="Monthly financial report reminder process">

      <startEvent id="theStart" />

      <sequenceFlow id="flow1" sourceRef="theStart" targetRef="writeReportTask" />

      <userTask id="writeReportTask" name="Write monthly financial report" >
        <documentation>
          Write monthly financial report for publication to shareholders.
        </documentation>
        <potentialOwner>
          <resourceAssignmentExpression>
            <formalExpression>accountancy</formalExpression>
          </resourceAssignmentExpression>
        </potentialOwner>
      </userTask>

      <sequenceFlow id="flow2" sourceRef="writeReportTask" targetRef="verifyReportTask" />

      <userTask id="verifyReportTask" name="Verify monthly financial report" >
        <documentation>
          Verify monthly financial report composed by the accountancy department.
          This financial report is going to be sent to all the company shareholders.
        </documentation>
        <potentialOwner>
          <resourceAssignmentExpression>
            <formalExpression>management</formalExpression>
          </resourceAssignmentExpression>
        </potentialOwner>
      </userTask>

      <sequenceFlow id="flow3" sourceRef="verifyReportTask" targetRef="theEnd" />

      <endEvent id="theEnd" />

    </process>

</definitions>

启动流程实例

现在,我们已经创建了业务流程的流程定义,通过这样的流程定义,我们可以创建流程实例,在这种情况下,一个流程实例将与特定月份的单个财务报告的创建和验证相匹配,所有流程实例共享相同的流程定义。

为了能够根据给定的流程定义创建流程实例,我们必须首先部署该流程定义,部署流程定义意味着两件事:

  • 流程定义将存储在为你的Activiti引擎配置的持久性数据存储中,因此,通过部署业务流程,我们确保引擎重新启动后,引擎将找到流程定义。
  • BPMN 2.0流程文件将解析为内存中的对象模型,可以通过Activiti API对其进行操作。

部署可以通过多种方式进行,一种方法是通过以下API,请注意,与Activiti引擎的所有交互都是通过其服务进行的。

Deployment deployment = repositoryService.createDeployment()
  .addClasspathResource("FinancialReportProcess.bpmn20.xml")
  .deploy();

现在,我们可以使用在流程定义中定义的id(请参阅XML文件中的process元素)来启动新流程实例,请注意,Activiti术语中的此id称为key

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("financialReport");

这将创建一个流程实例,该实例将首先经历启动事件,启动事件之后,它遵循所有输出顺序流(在这种情况下只有一个),并且到达了第一个任务(写每月财务报告),Activiti引擎现在将在持久数据库中存储任务,此时,将解决任务中附加的用户或组分配,并将其存储在数据库中,重要的是要注意,Activiti引擎将继续执行流程,直到达到等待状态(例如用户任务)为止。在这种等待状态下,流程实例的当前状态存储在数据库中,它将保持该状态,直到用户决定完成其任务为止,届时,引擎将继续运行直到达到新的等待状态或流程结束,同时,如果引擎重新启动或崩溃,则该流程的状态是安全的,并且在数据库中也处于良好状态。

创建任务后,由于用户任务活动处于等待状态,因此startProcessInstanceByKey方法将返回。在这种情况下,将任务分配给一个组,这意味着该组中的每个成员都是执行任务的候选人。

现在,我们可以将所有内容放在一起,并创建一个简单的Java程序,创建一个新的Eclipse项目,并将Activiti JAR和依赖项添加到其类路径(可以在Activiti发行版的libs文件夹中找到它们)。在调用Activiti服务之前,我们必须首先构造一个ProcessEngine,使我们能够访问这些服务。在这里,我们使用“standalone”配置,该配置构造一个ProcessEngine,该ProcessEngine使用也是演示设置中使用的数据库。

public static void main(String[] args) {

  // Create Activiti process engine
  ProcessEngine processEngine = ProcessEngineConfiguration
    .createStandaloneProcessEngineConfiguration()
    .buildProcessEngine();

  // Get Activiti services
  RepositoryService repositoryService = processEngine.getRepositoryService();
  RuntimeService runtimeService = processEngine.getRuntimeService();

  // Deploy the process definition
  repositoryService.createDeployment()
    .addClasspathResource("FinancialReportProcess.bpmn20.xml")
    .deploy();

  // Start a process instance
  runtimeService.startProcessInstanceByKey("financialReport");
}

任务列表

现在,我们可以通过添加以下逻辑通过TaskService检索此任务:

List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit").list();

请注意,我们传递给此操作的用户必须是accountancy的成员,因为它是在流程定义中声明的:

<potentialOwner>
  <resourceAssignmentExpression>
    <formalExpression>accountancy</formalExpression>
  </resourceAssignmentExpression>
</potentialOwner>

我们还可以使用任务查询API,通过组名获得相同的结果,现在,我们可以在代码中添加以下逻辑:

TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();

领取任务

会计现在需要申领任务,通过领取任务,特定用户将成为任务的受让人,并且该任务将从accountancy组其他成员的每个任务列表中消失,领取任务是通过编程完成的,如下所示:

taskService.claim(task.getId(), "fozzie");

现在,该任务已在领取该任务的个人任务列表中。

List<Task> tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();

完成任务

会计现在可以开始处理财务报告了,报告完成后,他就可以完成任务,这意味着该任务的所有工作都已完成。

taskService.complete(task.getId());

对于Activiti引擎,这是一个外部信号,表明必须继续执行流程实例,任务本身已从运行时数据中删除,接下来是任务的单个传出过渡,将执行移至第二个任务(“verification of the report”)。现在将使用与针对第一个任务所述的相同机制来分配第二个任务,所不同的只是将任务分配给管理组。

结束流程

可以按照与以前完全相同的方式检索并领取任务,完成第二个任务会将流程执行移至结束事件,从而结束流程实例,流程实例和所有相关的运行时执行数据将从数据存储中删除。

通过编程,你还可以使用historyService验证该流程是否已结束:

HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());

代码概述

合并之前各节中的所有代码片段,你应该具有类似的内容:

public class TenMinuteTutorial {

  public static void main(String[] args) {

    // Create Activiti process engine
    ProcessEngine processEngine = ProcessEngineConfiguration
      .createStandaloneProcessEngineConfiguration()
      .buildProcessEngine();

    // Get Activiti services
    RepositoryService repositoryService = processEngine.getRepositoryService();
    RuntimeService runtimeService = processEngine.getRuntimeService();

    // Deploy the process definition
    repositoryService.createDeployment()
      .addClasspathResource("FinancialReportProcess.bpmn20.xml")
      .deploy();

    // Start a process instance
    String procId = runtimeService.startProcessInstanceByKey("financialReport").getId();

    // Get the first task
    TaskService taskService = processEngine.getTaskService();
    List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
    for (Task task : tasks) {
      System.out.println("Following task is available for accountancy group: " + task.getName());

      // claim it
      taskService.claim(task.getId(), "fozzie");
    }

    // Verify Fozzie can now retrieve the task
    tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();
    for (Task task : tasks) {
      System.out.println("Task for fozzie: " + task.getName());

      // Complete the task
      taskService.complete(task.getId());
    }

    System.out.println("Number of tasks for fozzie: "
            + taskService.createTaskQuery().taskAssignee("fozzie").count());

    // Retrieve and claim the second task
    tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
    for (Task task : tasks) {
      System.out.println("Following task is available for management group: " + task.getName());
      taskService.claim(task.getId(), "kermit");
    }

    // Completing the second task ends the process
    for (Task task : tasks) {
      taskService.complete(task.getId());
    }

    // verify that the process is actually finished
    HistoryService historyService = processEngine.getHistoryService();
    HistoricProcessInstance historicProcessInstance =
      historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
    System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());
  }

}

进一步增强

很容易看出,这个业务流程太简单了,无法在现实中使用。但是,当你遍历Activiti中可用的BPMN 2.0构造时,你将能够通过以下方式增强业务流程:

  • 定义充当决策的网关,这样,经理可以拒绝财务报告,将为会计重新创建财务报告的任务。
  • 声明和使用变量,以便我们可以存储或引用报告,以便可以在表单中将其可视化。
  • 在流程结束时定义服务任务,将报告发送给每个股东。


博弈
2.5k 声望1.5k 粉丝

态度决定一切


引用和评论

0 条评论