网路冷眼@BlogJava

熙熙攘攘一闲人 以冷静的眼光观察技术
posts - 88, comments - 193, trackbacks - 0, articles - 28
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Chapter 7. BPMN

Table of Contents

What is BPMN?(何为BPMN)
Examples(示例)
Defining a process(定义一个流程)
Getting started: 10 minute tutorial(开始:10分钟教程)
Use case(用例)
Process diagram(流程图)
XML representation(XML表示)
Starting a process instance(启动一个流程示例)
Task lists(任务列表)
Claiming the task(领取任务)
Completing the task(完成任务)
Ending the process(终止流程)
Future enhancements(未来加强)
BPMN 2.0 constructs(BPMN 2.0构件)
Custom extensions(定制扩展)
Events(事件)
Start events(启动事件)
None start event(None启动事件)
End events(结束事件)
None end event(None结束事件)
Sequence flow(顺序流)
Conditional sequence flow(条件顺序流)
Gateways(网关)
Exclusive gateway(唯一网关)
Parallel Gateway(并行网关)
User task(用户任务)
Script Task(脚本服务)
Java Service Task(Java服务任务)
Execution listener(执行监听)
Task listener(任务监听器)
Email task(Email 任务)
Manual Task(手动任务)
Java receive task(Java接受任务)
Boundary events(边界事件)
Timer Boundary Event(定时器边界事件)
SubProcess(子流程)
Call activity (subprocess)(调用活动(子流程))

What is BPMN?(何为BPMN)

See our FAQ entry on BPMN 2.0.

参见我们的 FAQ entry on BPMN 2.0

Examples(示例)

Examples for the BPMN 2.0 constructs described in the following sections can be found in the workspace/activiti-x-examples folders of the Activiti distribution.

BPMN构件的示例

See the specific section on examples for more information.

详情参见 examples的某些章节。

Defining a process(定义一个流程)

To create a new BPMN 2.0 process definition, it's best to have your Eclipse properly set up.

为了建立新的BPMN 2.0流程定义,最好将Eclipse设置好properly set up

Create a new XML file (rightclick on any project and select New->Other->XML-XML File) and give it a name. Make sure that the file ends with .bpmn20.xml, since otherwise the engine won't pick up this file for deployment.

建立一个新的XML文件(在任何项目上右击并选择New->Other->XML-XML File)并给定一个名字。确保文件以.bpmn20.xml结尾,因为否则引擎在部署时不会部署这个文件。

new.bpmn.procdef

The root element of the BPMN 2.0 schema is the definitions element. Within this element, multiple process definitions can be defined (although we advise to have only one process definition in each file, since this simplifies maintenance later in the development process). An empty process definition looks as follows. Note that the minimal definitions element only needs the xmlns and targetNamespace declaration. The targetNamespace can be anything, and is useful for categorizing process definitions.

BPMN 2.0 结构的根元素是 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>

The process element has two attributes:

流程元素有两个属性:

  • id: this attribute is required and maps to the key property of an Activiti ProcessDefinition object. This id can then be used to start a new process instance of the process definition, through the startProcessInstanceByKey method on the RuntimeService. This method will always take the latest deployed version of the process definition.

    id: 本属性是必需的,并映射为Activiti 对象 ProcessDefinitionkey特性。通过RuntimeService ProcessDefinition 方法, id能够用来启动一个流程定义的一个新的流程实例。这个方法将总是采用流程定义的最新部署的版本

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

    Important to note here is that this is not the same as calling the startProcessInstanceById method. This method expects the String id that was generated at deploy time by the Activiti engine, and can be retrieved by calling the processDefinition.getId() method. The format of the generated id is 'key:version', and the length isconstrained to 64 characters. If you get an ActivitiException stating that the generated id is too long, limit the text in the key field of the process.

    在此重要注意之点是这个方法和调用startProcessInstanceById方法并不相同。这个方法猜想字符串id是由Activiti引擎在部署时产生,并能通过调用 processDefinition.getId()方法检索。产生的id的格式是 'key:version',长度包含64个字符。如果你得到一个 ActivitiException,说明产生id太长,那么在流程 key 字段限制文本的长度。

  • name: this attribute is optional and maps to the name property of a ProcessDefinition. The engine itself doesn't use this property, so it can for example be used for displaying a more human-friendly name in a user interface.

    name:本属性可选,并映射为ProcessDefinition.的name特性。因为引擎自身并不使用这个特性,所以它能够作为示例,在用户界面显示更人性化的名字。

Getting started: 10 minute tutorial(开始:10分钟教程)

In this section we will cover a (very simple) business process that we will use to introduce some basic Activiti concepts and the Activiti API.

本节我们将要讨论一个(非常简单)的业务流程。我们将要用这个业务流程介绍一些Activiti的基本概念及其API。

Use case(用例)

The use case is straightfoward: we have a company, let's call it BPMCorp. In BPMCorp, a financial report needs to be written every month for the company shareholders. This is the responsibility of the accountancy department. When the report is finished, one of the members of the upper management needs to approve the document before it is sent to all the shareholders.

本用例直截了当:我们开了一家公司,那我们叫他BPMCorp公司吧。在 BPMCorp公司,每个月需要给公司股东撰写一个财务报告。这是财务部门的职责。当报告完成后,在把它发送给所有的股东之前,需要上级管理层其中一个成员批准这个文档。

All files and code snippets used through the next sections can be found in the examples shipped with the Activiti distribution. Look for the packageorg.activiti.examples.bpmn.usertask.

贯穿后面章节的所有文件和代码片段在Activiti发行包的 examples 里面找到。请查找包 org.activiti.examples.bpmn.usertask

Process diagram(流程图)

The business process as described above, can be graphically visualized using the Activiti Modeler. The end result, in BPMN 2.0 notation, looks like this:

如上描述的业务流程能用 Activiti Modeler来对它进行图形可视化建模。以 BPMN 2.0的概念, 最终结果如下所示。

financial.report.example.diagram

There is nothing spectacular here. What we see is a none start event (circle on the left), followed by two user tasks: 'Write monthly financial report' and 'Verify monthly financial report', ending in a none end event (circle with thick border on the right).

这里并不壮观。我们所见的是none start eventnone start event (左边圆圈),接着是两个用户任务user tasks:'Write monthly financial report' and 'Verify monthly financial report',以一个none end event(右边粗线的圆圈)结束。

XML representation(XML表示)

The XML version of this business process (FinancialReportProcess.bpmn20.xml) looks as shown below. It's easy to recognize the main elements of our process (click on the links for going to the detailed section of that BPMN 2.0 construct):

业务流程 (FinancialReportProcess.bpmn20.xml)的XML版本如下所示。很容易识别流程的主要元素(点击链接,将导航至BPMN构件的细节部分):

  • The (none) start event learns us where to start with the process

    (none) start event 了解我们从哪个地方启动流程。

  • The user tasks declarations are the representation of the human tasks of our process. Note that the first task is assigned to the accountancy group, while the second task is assigned to the management group. See the section on user task assignment for more information on how users and groups can be assigned to user tasks.

    user tasks 声明是流程的人物任务的代表。注意第一个任务分配给组,而第二个任务分配给了组。详情参见the section on user task assignment , 以了解如何给用户和组分配任务。

  • The process ends when the none end event is reached.

    当到达 none end event时,流程终止。

<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>
Starting a process instance(启动一个流程示例)

We now have defined the process definition of our business process. From such a process definition, we can create at runtime process instances. In this case, one process instance would match with the creation and verification of the financial report every month.

现在我们已经定义了业务流程的流程定义。从如此一个流程定义里,我们能建立一个运行时流程实例。在这种情况下,一个流程实例每月将建立并验证财务报告。

To be able to create process instances from a given process definition, we must first deploy this process definition. Deploying a process definition means two things:

为了能够从一个给定的流程定义建立流程实例,我们必须首先部署这个流程定义。部署流程定义意味著如下两件事:

  • The process definition will be stored in the persistent datastore that is configured for your Activiti engine. So by deploying our business process, we make sure that the engine will find the process definition after an engine reboot.

    流程定义将保存在为Activiti引擎配置的持久化数据存储中。这样通过部署业务流程,确保在引擎重新引导后,引擎将找到流程定义。

  • The BPMN 2.0 process file will be parsed to an in-memory object model that can be manipulated through the Activiti API.

    BPMN 2.0 流程文件将被解析为一个能够通过Activiti API操作的内存对象模型。

More information on deployment can be found in the dedicated section on deployment.

有关部署的详情在deployment里面专门论述。

As described in that section, deployment can happen in several ways. One way is through the API as follows:

in that section所述,以几种方式完成部署。其中通过如下的API:

Deployment deployment = repositoryService.createDeployment()
  .addClasspathResource("org/activiti/examples/bpmn/usertask/FinancialReportProcess.bpmn20.xml")
  .deploy();

Now we can start a new process instance using the id we defined in the process definition (see first line of the XML). Note that this id in Activiti terminology is called the key.

现在,我们能够通过在流程定义里定义的id来启动一个新的流程实例(参见XML文件的首行)。注意以Activiti的术语, id叫做 key

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

This will create a runtime execution that will go first through the start event. After the start event, it follows all the outgoing sequence flow (only one in this case) and the first task ('write monthly financial report') is reached. The Activiti engine will now store a task in the persistent datastore. At this point, the user or group assignments attached to the task are resolved and also stored in the datastore.

这将建立一个运行时期执行。这个执行首先通过启动事件。在启动事件之后,接着所有的即将离去的顺序流(本例只有一个)。而后到达的是第一个任务('write monthly financial report')。Activiti引擎现在将一个任务保存在持久化数据存储里面。在这点上,任务附属的用户或者用户组分配也存储在数据存储里面。

After the task is created, the startProcessInstanceByKey will return since the user task activity is a so-called wait state, which means that the engine will continue the process instance only when some external trigger is given. In this case, the task is assigned to a group, which means that the every member of the group is a candidate to perform the task.

在建立任务之后,因为这个用户任务活动是一个所谓的等待状态( wait state),所以 startProcessInstanceByKey 将返回。等待状态意味著只有当给定外部触发器时,引擎才将继续流程实例。在这种情况下,任务分派给一个组,这意味着组中的每个人都是执行这个任务的候选人

Task lists(任务列表)

We can now retrieve this newly created task through the taskService.

通过 taskService.,我们现在能检索新建的任务。

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

Note that the user we pass to this operation needs to be a member of the accountancy group, since that was declared in the process definition:

注意我们传递给这个操作的用户必须是 组的成员,因为在流程定义里面是这样声明的:

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

We could also use the task query API to get the same results using the name of the group:

我们也能够通过使用组名采用任务查询API得到相同的结果:

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

The business process described here is also deployed as an example to the demo setup database. After running the demo setup, log into the Activiti Explorer as fozzie (he's an accountant), select the Processes page and and click on the 'Start Process' link in the 'Actions' column corresponding to the 'Monthly financial report' process.

此处定义的业务流程也作为demo setup数据库实例进行部署。在运行demo setup之后,以fozzie(他是一个会计)登录到Activiti Explorer,选择Processes页面并点击和 'Monthly financial report'流程相关的 'Actions' 列里的'Start Process' 连接。

bpmn.financial.report.example.start.process

As explained, the process will execute up to the first user task. Since we're logged in as fozzie, we can see that there is a new candidate task available for him after we've started the process instance. Select the Tasks page to view this new task. Note that even if the process was started by someone else, the task would still be visible as a candidate task to everyone in the accountancy group.

作为解释,流程将执行到第一个用户任务。因为我们作为fozzie登录,在我们已经启动这个流程引擎之后,我们能看见他可获得一个候选任务。选择Task页面查看这个新任务。注意尽管是其他人启动这个任务的,但是这个任务仍将作为换一个候选任务对于会计组内的任何人可见。

bpmn.financial.report.example.task.assigned

Claiming the task(领取任务)

An accountant now needs to claim the task. By claiming the task, the specific user will become the assignee of the task and the task will disappear from every task list of the other members of the accountancy group. Claiming a task is programmatically done as follows:

一个会计现在需要领取任务claim the task)。通过领取任务,某一用户将成为任务的责任人,这个任务将从会计组的其他成员的任务列表里面消失。对领取任务的编程实现如下:

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

The task is now in the personal task list of the one that claimed the task.

现在这个任务成为任务领取人的个人任务(personal task list of the one that claimed the task

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

In the Activiti Explorer UI, clicking the claim button will call the same operation. The task will now move to the personal task list of the logged on user.

在Activiti Explorer UI里面,点击 claim按钮将调用相同的操作。现在任务将移动到登录用户的个人任务里面。

bpmn.financial.report.example.claim.task

Completing the task(完成任务)

The accountant can now start working on the financial report. Once the report is finished, he can complete the task, which means that all work for that task is done.

现在会计能够开始从事金融报告。一旦任务完成,他能够完成任务( complete the task),这意味著那个任务的所有工作均完成。

taskService.complete(task.getId());

For the Activiti engine, this is an external signal that the process instance execution must be continued. The task itself is removed from the runtime data. The single outgoing transition out of the task is followed, bringing the execution in the second task ('verification of the report'). The same mechanism as described for the first task will now happen, with the small difference that the task will be assigned to the management group.

对于Activiti引擎来说,这是流程引擎必须继续执行的一个外部信号。任务自身被从运行期数据里面删除。紧接着是单个的任务即将离去的状态转换,从而带来了第二个任务 ('verification of the report')的执行。将要发生的事正如第一个任务描述的相同的机制。唯一的区别是任务分配给管理(management)组。

In the demo setup, completing the task is done by clicking the complete button in the task list. Since Fozzie isn't an accountant, we need to log out of the Activiti Explorer and login in as kermit (which is a manager). The second task is now visible in the unassigned task lists.

在demo setup里,通过点击任务列表的 complete 按钮执行完成任务。因为Fozzie是一个会计,所以我们需要从Activiti Explorer里注销并以kermit(他是一个经理)登录。第二个任务出现在未分配任务列表里面。

bpmn.financial.report.example.complete.task

Ending the process(终止流程)

The verification task can be retrieved and claimed in exactly the same way as before. Completing this second task, will bring process execution at the end event, which finishes the process instance. The process instance and all related runtime execution data is removed from the datastore.

完全采用先前同样的方法能够检索和认领验证任务。完成第二个任务之后,将带来终止任务的流程执行。终止任务完成这个流程实例。流程实例和所有相关的执行数据从数据存储中删除。

When you log into Activiti Probe you can verify this, since no records will be found in the table where the process executions are stored.

当你登录到Activiti Probe里,你能验证这个事实,因为流程执行所保存的库表中找不到任何记录。

bpmn.financial.report.example.process.ended

Future enhancements(未来加强)

It's easy to see that this business process is too simple to be usable in reality. However, as you are going through the BPMN 2.0 constructs available in Activiti, you will be able to enhance the business process by

很容易看到,这个业务流程太过简单,无法在现实中堪以重用。但是,正如你将浏览在Activiti里面的BPMN 2.0构件,你将能通过如下方法加强业务流程的功能,

  • defining a timer start event that automatically starts a process instance at the end of every month.

    定义一个定时器启动事件timer start event ),它将在每个月末启动一个流程实例。

  • defining gateways that act as decisions. This way, a manager could reject the financial report which would recreate the task for the accountant.

    定义扮演决策的网关gateways)。这种方法,一个经理可能这个金融报告,这个方法将为会计重新产生这个被驳回的任务。

  • declaring and using variables, such that we can store or reference the report so that it can be visualized in the form.

    声明并使用变量variables),使得我们能够保存或者引用这个报告以便在表单里面被可视化。

  • defining a service task at the end of the process that will send the report to every shareholder.

    在流程结束时定义一个服务任务( service task ),它将把报告发送给每个股东。

  • etc.

    等等。

BPMN 2.0 constructs(BPMN 2.0构件)

Custom extensions(定制扩展)

The BPMN 2.0 standard is a good thing for all parties involved. End-users don't suffer from a vendor lock-in that comes by depending on a proprietary solution. Frameworks, and particularly open-source frameworks such as Activiti, can implement a solution that has the same (and often better implemented ;-) features as those of a big vendor. Due to the BPMN 2.0 standard, the transition from such a big vendor solution towards Activiti is an easy and smooth path.

BPMN 2.0标准对参与各方而言是皆大欢喜的好事。最终用户不会遭遇到供应商锁定而依赖专有解决方案。框架,特别是诸如Activiti这样的开源框架,能够实现像那些大供应商相同的(经常是更好实现的)解决方案。承BPMN 2.0标准所赐,从一个大供应商迁移至Activiti才这样轻便和平滑。

The downside of a standard however, is the fact that it is always the result of many discussions and compromises between different companies (and often visions). As a developer reading the BPMN 2.0 XML of a process definition, sometimes it feels like certain constructs or way to do things are too cumbersome. Since Activiti puts ease of development as a top-priority, we introduced something called the 'Activiti BPMN extensions'. These 'extensions' are new constructs or ways to simplify certain constructs, that are not in the BPMN 2.0 specification.

但是标准的劣势是这样的事实,它总是不同公司之间多次讨论和折中的结果。作为一个开发者,阅读一个流程定义的BPMN 2.0 XML时,有时某些构件或者做事的方式太过累赘。因为Activiti把减轻开发者作为头等大事,所以我们引入了所谓的 'Activiti BPMN extensions'。这些扩展是新的构件或者是简化某些构件的方法,它们不在BPMN 2.0规范中定义。

Although the BPMN 2.0 specification clearly states that it was made for custom extension, we make sure that:

尽管BPMN 2.0规范清楚表明为定制扩展而建立,但是我们深信:

  • The prerequisite of such a custom extension is that there always must be a simple transformation to the standard way of doing things. So when you decide to use a custom extension, you don't have to be afraid that there is no way back.

    定制扩展的先决条件是总是必须存在转换到标准方式做事上面的简单方法。所以当你决定采用定制扩展时,不必害怕没有后悔药可吃(没有办法返回)。

  • When using a custom extension, this is always clearly indicated by giving the new XML element, attribute, etc. the activiti: namespace prefix.

    当使用定制扩展,这总是用新的XML元素,属性等等清楚指示这一点。例如, activiti: namespace 前缀。

  • The goal of these extensions is to eventually push them back into a next version of the BPMN specification, or at least trigger a discussion that can lead to a revision of that specific BPMN construct.

    这些扩展的目标最终推动它们返回到BPMN规范的下一个版本中,或者至少触发能导致特定BPMN构件的修订的讨论。

So whether you want to use a custom extension or not, is completely up to you. Several factors will influence this decision (graphical editor usage, company policy, etc.). We only provide them since we believe that some points in the standard can be done simpler or more efficient. Feel free to give us (positive and/or negative) feedback on our extensions, or to post new ideas for custom extensions. Who knows, some day your idea might pop up in the specification!.

所以,是否使用定制扩展,完全由你掌控。几个因素将会影响这个决定(图形化编辑器的使用,公司策略,等等)。我们提供这些仅仅是因为我们相信在标准里的某些点能够更简单或者有效地完成。请自由地对我们的扩展提供(正面和/或负面)的反馈,或者为定制扩展公布新的想法。谁能料到,某天你的想法可能在规范里赫然在目!

Events(事件)

Events are used to model something that happens during the lifetime process. Events are always visualized as a circle. In BPMN 2.0, there exist two main event categories:catching or throwing event.

事件是在生命过程中发生的用在模型的那些东西。事件总是可视化为一个圆圈。存在两种主要的事件分类:捕获(catching)或者抛出(throwing )事件。

  • Catching: when process execution arrives in the event, it will wait for a trigger to happen. The type of trigger is defined by the inner icon or the type declaration in the XML. Catching events are visually differentiated from a throwing event by the inner icon that is not filled (i.e. it is white).

    Catching: 当流程执行到达某个事件里,它将等待一个触发器发生。通过内部图标或者在XML的类型定义来定义这个触发器类型。通过没有填充的内部图标(例如,它是白色),捕获事件在可视化方面区分抛出事件的差别。

  • Throwing: when process execution arrives in the event, a trigger is fired. The type of trigger is defined by the inner icon or the type declaration in the XML. Throwing events are visually differentiated from a catching event by the inner icon that is filled with black.

    Throwing: 当流程执行到达这个事件里,一个触发器被触发。通过内部图标或者在XML的类型定义来定义触发器类型。通过填充黑色的内部图标,抛出事件在可视化方面区分捕获事件的差别。

Start events(启动事件)

A start event indicates where a process starts. The type of start event (process starts on arrival of message, on specific time intervals, etc.), defining how the process is started is shown as a small icon in the visual representation of the event. In the XML representation, the type is given by the declaration of a sub-element.

启动事件指定在哪里开始启动流程。启动事件的类型(当消息到达时流程启动,在特定事件间隔启动,等等),定义流程如何启动,在事件可视化表示时以一个小图标显示。在XML表达时,通过一个子元素声明给定类型。

Start events are always catching: conceptually the event is (at any time) waiting until a certain trigger happens.

启动事件总是捕获事件(are always catching:从概念上讲,事件一直(任何时候)等待直到某一触发器发生。

In a start event, following activiti-specific properties can be specified:

  • formKey: references to a form template that users have to fill in when starting a new process instance. More information can be found in the forms section Example:

    formKey:当启动一个新的流程实例时,引用到一个用户不得不填充的表单模板。详情能在the forms section 找到。

    <startEvent id="request" activiti:formKey="org/activiti/examples/taskforms/request.form" />
  • initiator: identifies the variable name in which the authenticated user id will be stored when the process is started. Example:

    发起人(initiator): 当启动流程时,辨识存储认证用户id的变量名。例如:

    <startEvent id="request" activiti:initiator="initiator" />

    The authenticated user must be set with the method IdentityService.setAuthenticatedUserId(String) in a try-finally block like this:

    在try-finally 块里必须用方法IdentityService.setAuthenticatedUserId(String)来设置认证用户。

    try {
      identityService.setAuthenticatedUserId("bono");
      runtimeService.startProcessInstanceByKey("someProcessKey");
    } finally {
      identityService.setAuthenticatedUserId(null);
    }
    

    This code is baked into the Activiti Explorer application. So it works in combination with ???

    这个代码被拷贝至Activiti Explorer应用程序里。所以它能和 ??? 一道组合工作。

None start event(None启动事件)
Description(描述)

A 'none' start event technically means that the trigger for starting the process instance is unspecified. This means that the engine cannot anticipate when the process instance must be started. The none start event is used when the process instance is started through the API by calling one of the startProcessInstanceByXXX methods.

从技术上讲,一个'none'启动事件意味着没有指定启动流程实例的触发器。这意味着引擎不能预料何时必须启动流程实例。当通过API由调用startProcessInstanceByXXX 方法启动流程实例时,使用none启动事件。

ProcessInstance processInstance = runtimeService.startProcessInstanceByXXX();

Note: a subprocess always has a none start event.

注意:子流程总有一个none start event.

Graphical notation(图形符号)

A none start event is visualized as a circle with no inner icon (i.e. no trigger type).

用空心圆圈可视化none启动事件。(即无触发类型)。

bpmn.none.start.event

XML representation(XML表示)

The XML representation of a none start event is the normal start event declaration, without any sub-element (other start event types all have a sub-element declaring the type).

none启动事件的XML表示是正常的启动事件声明,不带任何子元素(其它的启动事件所有都具有一个声明类型的子元素)。

<startEvent id="start" name="my start event" />
End events(结束事件)

An end event signifies the end (of a path) of a (sub)process. An end event is always throwing. This means that when process execution arrives in the end event, a result is thrown. The type of result is depicted by the inner black icon of the event. In the XML representation, the type is given by the declaration of a sub-element.

一个结束事件象征一个路径或者一个子流程的结束。一个结束事件总是抛出类型always throwing)。这意味着当流程执行到达结束事件里,抛出结果。通过事件的黑色图标描述结果类型。在XML表示里,通过子元素的声明来给定类型。

None end event(None结束事件)
Description(描述)

A 'none' end event means that the result thrown when the event is reached is unspecified. As such, the engine will not do anything extra besides ending the current path of execution.

‘none’结束事件意味着当没有指定到达的事件时,抛出结果。这样,引擎只有终止执行的当前路径。

Graphical notation(图形符号)

A none end event is visualized as a circle with a thick border with no inner icon (no result type).

none结束事件用不带内部图标的粗线圆圈表示(没有结果类型)。

bpmn.none.end.event

XML representation(XML表示)

The XML representation of a none end event is the normal end event declaration, without any sub-element (other end event types all have a sub-element declaring the type).

none结束事件的XML表示是一般的结束事件声明,不带任何子元素(其它的事件所有都具有声明类型的子元素)。

<endEvent id="end" name="my end event" />
Sequence flow(顺序流)
Description(描述)

A sequence flow is the connector between two elements of a process. After an element is visited during process execution, all outgoing sequence flow will be followed. This means that the default nature of BPMN 2.0 is to be parallel: two outgoing sequence flow will create two separate, parallel paths of execution.

顺序流是两个流程元素之间的连接器。在流程执行期间,在访问一个元素之后,将紧跟所有的即将离开的顺序流。这意味著BPMN 2.0的缺省性质是并行的:两个离开的顺序流将建立两个分开的,并行的执行路径。

Graphical notation(图形符号)

A sequence flow is visualized as an arrow going from the source element towards the target element. The arrow always points towards the target.

顺序流可视化为从源元素到目标元素的一个箭头。箭头总是指向目标元素。

bpmn.sequence.flow

XML representation(XML表示)

Sequence flow need to have a process-unique id, and a reference to an existing source and target element.

顺序流需要具有一个流程唯一的id,和指向业已存在的目标元素的引用。

<sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
Conditional sequence flow(条件顺序流)
Description(描述)

A sequence flow can have a condition defined on it. When a BPMN 2.0 activity is left, the default behavior is to evaluate the conditions on the outgoing sequence flow. When a condition evaluates to true, that outgoing sequence flow is selected. When multiple sequence flow are selected that way, multiple executions will be generated and the process will be continued in a parallel way.

顺序流能在其上定义一个条件表达式。当一个BPMN 2.0的活动离开时,缺省行为是估计即将离开的顺序流上的条件。 当条件估计为true,那么选择离开的顺序流。当选择多个顺序流,将产生多个执行并且以并行方式继续这个流程。

Note: the above holds for BPMN 2.0 activities (and events), but not for gateways. Gateways will handle sequence flow with conditions in specific ways, depending on the gateway type.

注意:上述针对BPMN 2.0的活动(和事件),不是针对网关。依赖网关的类型,网关以特定的方式处理带有条件的顺序流。

Graphical notation(图形符号)

A conditional sequence flow is visualized as a regular sequence flow, with a small diamond at the beginning. The condition expression is shown next to the sequence flow.

条件顺序流作为一个正常的顺序流可视化,开始带有一个宝石形状。条件表达式显示在顺序流之上。

bpmn.conditional.sequence.flow

XML representation(XML表示)

A conditional sequence flow is represented in XML as a regular sequence flow, containing a conditionExpression sub-element. Note that for the moment onlytFormalExpressions are supported, Omitting the xsi:type="" definition will simply default to this only supported type of expressions.

在XML里,条件顺序流作为一个正常的顺序流表示,包含一个conditionExpression子元素。注意目前只支持tFormalExpressions,忽略 xsi:type=""定义,这将对只支持的表达式类型是简单的缺省的。

<sequenceFlow id="flow" sourceRef="theStart" targetRef="theTask">
  <conditionExpression xsi:type="tFormalExpression">
    <![CDATA[${order.price > 100 && order.price < 250}]]>
  </conditionExpression>
</sequenceFlow>

Currently conditionalExpressions can only be used with UEL, detailed info about these can be found in section Expressions. The expression used should resolve to a boolean value, otherwise an exception is thrown while evaluating the condition.

当前只能用UEL( only be used with UEL)使用条件表达式,有关的详情参见章节 Expressions。使用的表达式应当解析为布尔值,否则当估计条件时抛出异常。

  • The example below references data of a process variable, in the typical JavaBean style through getters.

    下例通过getter方法以JavaBean方式引用了一个流程变量的数据。

  • This example invokes a method that resolves to a boolean value.

    这个示例调用解析为布尔值的方法。

    <conditionExpression xsi:type="tFormalExpression">
      <![CDATA[${order.isStandardOrder()}]]>
    </conditionExpression>

The Activiti distribution contains the following example process using value and method expressions (see org.activiti.examples.bpmn.expression):

Activiti发布包包含使用值和方法表达式的示例流程(参见 org.activiti.examples.bpmn.expression)

bpmn.uel-expression.on.seq.flow

Gateways(网关)

A gateway is used to control the flow of execution (or as the BPMN 2.0 describes, the tokens of execution). A gateway is capable of consuming or generating tokens.

网关用来控制执行的流向(或者正如BPMN 2.0所述,执行的令牌 )。网关有能力消费(consuming)或者生产(generating)令牌。

A gateway is graphically visualized as a diamond shape, with an icon inside. The icon shows the type of gateway.

网关以一个菱形表示,里面带有一个图标。这个图标显示了网关的类型。

bpmn.gateway

Exclusive gateway(唯一网关)
Description(描述)

An exclusive gateway (also called the XOR gateway or more technical the exclusive data-based gateway), is used to model a decision in the process. When the execution arrives at this gateway, all outgoing sequence flow are evaluated in the order in which they are defined. The sequence flow which condition evaluates to true (or which doesn't have a condition set, conceptually having a 'true' defined on the sequence flow) is selected for continuing the process.

一个唯一网关(也叫异或网关( XOR gateway)),或者更加技术些,互斥式基于数据的网关(exclusive data-based gateway)),用作流程里面的模型做决策。当执行到达这个网关,所有即将离去的顺序流被顺序估计。条件估计为true(或者它没有条件集合,从概念上讲,具有一个定义在这个顺序流上的‘true’值)的顺序流被选中得以继续这个流程。

Note that the semantics of outgoing sequence flow is different to that of the general case in BPMN 2.0. While in general all sequence flow which condition evaluates to true are selected to continue in a parallel way, only one sequence flow is selected when using the exclusive gateway. In case multiple sequence flow have a condition that evaluates to true, the first one defined in the XML (and only that one!) is selected for continuing the process. If no sequence flow can be selected, an exception will be thrown.

注意:输出顺序流的语义与BPMN 2.0通常情况不同。一般情况下,当条件表达式估计为真时,所有的顺序流选择以并行方式继续,当使用唯一网关时,只选中唯一一个顺序流。在多个条件都估计为真的顺序流情况时,只有在XML里面定义的第一个(当且仅当只有那一个)被选中来继续这个流程。如果不能选择顺序流,将抛出一个异常。

Graphical notation(图形符号)

An exclusive gateway is visualized as a typical gateway (i.e. a diamond shape) with an 'X' icon inside, referring to the XOR semantics. Note that a gateway without an icon inside defaults to an exclusive gateway. The BPMN 2.0 specification does not allow mixing the diamond with and without an X in the same process definition.

一个唯一网关以一个典型的网关方式可视化(即一个宝石符号)里面带有一个“X"图标,意味着异或( XOR )语义。注意里面没有图标的网关缺省是一个唯一网关。BPMN 2.0规范不允许在相同的流程定义里面混合使用带X和不带X。

bpmn.exclusive.gateway.notation

XML representation(XML表示)

The XML representation of an exclusive gateway is straight-forward: one line defining the gateway and condition expressions defined on the outgoing sequence flow. See the section on conditional sequence flow to see which options are available for such expressions.

唯一网关的XML表示是直截了当的:一行定义网关,条件条件表达式定义在即将离开的顺序流之上。为了了解可以获得这种条件表达式有哪些选项,请参考条件表达式章节(conditional sequence flow )。

Take for example the following model:

以下面的模型作为示例:

bpmn.exclusive.gateway

Which is represented in XML as follows:

用XML表示如下:

<exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" />
    
<sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="theTask1">
  <conditionExpression xsi:type="tFormalExpression">${input == 1}</conditionExpression>
</sequenceFlow>
    
<sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="theTask2">
  <conditionExpression xsi:type="tFormalExpression">${input == 2}</conditionExpression>
</sequenceFlow>
    
<sequenceFlow id="flow4" sourceRef="exclusiveGw" targetRef="theTask3">
  <conditionExpression xsi:type="tFormalExpression">${input == 3}</conditionExpression>
</sequenceFlow>
Parallel Gateway(并行网关)
Description(描述)

Gateways can also be used to model concurrency in a process. The most straightforward gateway to introduce concurrency in a process model, is the Parallel Gateway, which allows to fork into multiple paths of execution or join multiple incoming paths of execution.

网关也能在一个流程里用来对并发建模。在一个流程模型里引入并发最直接的网关,是并行网关Parallel Gateway),它允许 fork 执行多个路径,或者join 多个执行的到达路径。

The functionality of the parallel gateway is based on the incoming and outgoing sequence flow:

并行网关的功能性基于即将到达的和即将离开的顺序流:

  • fork: all outgoing sequence flow are followed in parallel, creating one concurrent execution for each sequence flow.

    fork: 所有即将离开的顺序流将以并行方式,为每个顺序流程建立一个并发执行。

  • join: all concurrent executions arriving at the parallel gateway wait in the gateway until an execution has arrived for each of the incoming sequence flow. Then the process continues past the joining gateway.

    join: 所有的并发执行到达并行网关,在网关里面等待直到每个来到的顺序流的执行到达。那时,流程继续通过合并网关。

Note that a parallel gateway can have both fork and join behavior, if there are multiple incoming and outgoing sequence flow for the same parallel gateway. In that case, the gateway will first join all incoming sequence flow, before splitting into multiple concurrent paths of executions.

注意:如果同一平行网关具有多个到达和离去顺序流,并行网关能够同时具有fork和join行为。那种情况,在分叉为执行的多个并发路径之前,网关首先合并所有来到的顺序流。

An important difference with other gateway types is that the parallel gateway does not evaluate conditions. If conditions are defined on the sequence flow connected with the parallel gateway, they are simply neglected.

和其它网关类型的最重要的区别是并行网关不估计条件。如果在用并行网关的顺序流之上定义条件,它们将被忽略。

Graphical Notation(图形符号)

A parallel gateway is visualized as a gateway (diamond shape) with the 'plus' symbol inside, referring to the 'AND' semantics.

并行网关可视化为一个里面带有‘+’号的网关(宝石形状),参考为'AND'语义。

bpmn.parallel.gateway

XML representation

Defining a parallel gateway needs one line of XML:

只需要一行XML就可定义并行网关:

<parallelGateway id="myParallelGateway" />

The actual behavior (fork, join or both), is defined by the sequence flow connected to the parallel gateway.

通过连接到并行网关的顺序流定义实际的行为(fork, join 或者两者)。

For example, the model above comes down to the following XML:

例如,上述的模型落地为下列XML:

    <startEvent id="theStart" />
    <sequenceFlow id="flow1" sourceRef="theStart" targetRef="fork" />
    
    <parallelGateway id="fork" />
    <sequenceFlow sourceRef="fork" targetRef="receivePayment" />
    <sequenceFlow sourceRef="fork" targetRef="shipOrder" />
    
    <userTask id="receivePayment" name="Receive Payment" />  
    <sequenceFlow sourceRef="receivePayment" targetRef="join" />
    
    <userTask id="shipOrder" name="Ship Order" /> 
    <sequenceFlow sourceRef="shipOrder" targetRef="join" />
    
    <parallelGateway id="join" />
    <sequenceFlow sourceRef="join" targetRef="archiveOrder" />
    
    <userTask id="archiveOrder" name="Archive Order" /> 
    <sequenceFlow sourceRef="archiveOrder" targetRef="theEnd" />
    
    <endEvent id="theEnd" />

In the above example, after the process is started, two tasks will be created:

在上例,当流程启动之后,将建立两个任务:

ProcessInstance pi = runtimeService.startProcessInstanceByKey("forkJoin");
TaskQuery query = taskService.createTaskQuery()
                         .processInstanceId(pi.getId())
                         .orderByTaskName()
                         .asc();

List<Task> tasks = query.list();
assertEquals(2, tasks.size());

Task task1 = tasks.get(0);
assertEquals("Receive Payment", task1.getName());
Task task2 = tasks.get(1);
assertEquals("Ship Order", task2.getName());

When these two tasks are completed, the second parallel gateway will join the two executions and since there is only one outgoing sequence flow, no concurrent paths of execution will be created, and only the Archive Order task will be active.

当这两个任务完成之后,第二个并行网关将加入到两个执行路径当中。这是因为只有一个离开的顺序流,将不建立执行的并发路径。并且只激活Archive Order

Note that a parallel gateway does not need to be 'balanced' (i.e. a matching number of incoming/outgoing sequence flow for corresponding parallel gateways). A parallel gateway will simply wait for all incoming sequence flow and create a concurrent path of execution for each outgoing sequence flow, not influenced by other constructs in the process model. So, the following process is legal in BPMN 2.0:

注意并行网关并不需要平衡()(例如为相关的并行网关匹配输入/输出的顺序流)。并行网关将知识等待所有的输入顺序流并为每个的输出流建立一个执行的并发路径,并不受流程模型其它构件的影响。所以,在BPMN 2.0里,下列流程是合法的。

bpmn.unbalanced.parallel.gateway

User task(用户任务)
Description(描述)

A 'user task' is used to model work that needs to be done by a human actor. When the process execution arrives at such a user task, a new task is created in the task list of the user(s) or group(s) assigned to that task.

用户任务(user task)用来为由人物角色完成的工作建模。当流程执行到达如此的一个用户任务,为分配给这个任务的用户或者用户组的任务列表里建立一个新任务。

Graphical notation(图形符号)

A user task is visualized as a typical task (rounded rectangle), with a small user icon in the left upper corner.

用户任务以一个典型的任务来可视化(圆角矩形),在左上方带有一个用户图标。

bpmn.user.task

XML representation(XML表示)

A user task is defined in XML as follows. The id attribute is required, the name attribute is optional.

用户任务以XML定义如下。id 属性必选,name 属性可选。

<userTask id="theTask" name="Important task" />     
      

A user task can have also a description. In fact any BPMN 2.0 element can have a description. A description is defined by adding the documentation element.

用户任务也可以有描述。事实上任何BPMN 2.0元素都能够具有描述。通过添加documentation 元素定义描述。

<userTask id="theTask" name="Schedule meeting" >
  <documentation>
   Schedule an engineering meeting for next week with the new hire.
 </documentation>

The description text can be retrieved from the task in the standard Java way:

以标准的Java方法可以从任务里检索描述文本:

task.getDescription()
User assignment(用户分配)

A user task can be directly assigned to a user. This is done by defining a humanPerformer sub element. Such a humanPerformer definition needs aresourceAssignmentExpression that actually defines the user. Currently, only formalExpressions are supported.

用户任务能够直接分配给用户。通过定义humanPerformer子元素来完成这个工作。实际上,这样一个 humanPerformer需要定义用户的resourceAssignmentExpression。当前只支持正式表达式formalExpressions

<process ... >
  
  ...
  
  <userTask id='theTask' name='important task' >
    <humanPerformer>
      <resourceAssignmentExpression>
        <formalExpression>kermit</formalExpression>
      </resourceAssignmentExpression>
    </humanPerformer>
  </userTask>

Only one user can be assigned as human performer to the task. In Activiti terminology, this user is called the assignee. Task that have an assignee are not visible in the task lists of other people, and are found in the so-called personal task list of the assignee.

只能把任务分配给一个用户作为人物执行者。以Activiti术语,这个用户叫责任人assignee)。已有责任人的任务在其它人的任务列表不可见,只能在责任人的所谓的个人任务列表personal task list) 中找到。

Tasks directly assigned to users can be retrieved through the TaskService as follows:

通过TaskService,可以检索直接分配给用户的任务。

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

Tasks can also be put in the so-called candidate task list of people. In that case, the potentialOwner construct must be used. The usage is similar to the humanPerformerconstruct. Do note that it is required to define for each element in the formal expression to specify if it is a user or a group (the engine cannot guess this).

也能将任务放置到人们所谓的候选任务列表( candidate task list )当中。在那种情况下,必须使用 potentialOwner 构件。它的使用和humanPerformer 构件相似。注意如果它是一个用户或者组(引擎不能猜到)必须在正规的表达式为每个元素定义。

<process ... >
  
  ...
  
  <userTask id='theTask' name='important task' >
    <potentialOwner>
      <resourceAssignmentExpression>
        <formalExpression>user(kermit), group(management)</formalExpression>
      </resourceAssignmentExpression>
    </potentialOwner>
  </userTask>

Tasks defines with the potential owner construct, can be retrieved as follows (or a similar TaskQuery usage as for the tasks with an assignee):

potential owner构件定义的任务,能够被如下方式检索(或者至于带有责任人任务,相似任务查询( TaskQuery )的用法):

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

This will retrieve all tasks where kermit is a candidate user, i.e. the formal expression contains user(kermit). This will also retrieve all tasks that are assigned to a group where kermit is a member of (e.g. group(management), if kermit is a member of that group and the Activiti identity component is used). The groups of the user are resolved at runtime and these can be managed through the IdentityService.

这将检索kermit是一个候选用户candidate user,)的所有任务,即正式表达式包含了用户user(kermit)。这也将检索分配给kermit所在组的所有任务(例如,如果kermit是这个组group(management)的成员,那么将使用Activiti身份组件)。组在运行时解析,这些能够通过身份服务来管理。

If no specifics are given whether the given text string is a user or group, the engine defaults to group. So the following would be the same as when group(accountancy) was declared.

如果没有特别指定给定文本字符串是一个用户或者组,引擎缺省为组。所以下面应当和声明为group(accountancy) 一样

<formalExpression>accountancy</formalExpression>
Activiti extensions for task assignment(任务分配的Activiti扩展)

It is clear that user and group assignments are quite cumbersome for use cases where the assignment is not complex. To avoid these complexities, custom extensions on the user task are possible.

显然,对于分配并不复杂的用例,用户和组分配时相当麻烦的。为了避免这些复杂性,可能在用户任务上定制扩展(custom extensions )。

  • assignee attribute: this custom extension allows to directly assign a user task to a given user.

    责任人属性(assignee attribute):这个定制扩展允许直接分配一个用户任务给一个指定用户。

    <userTask id="theTask" name="my task" activiti:assignee="kermit" />

    This is exactly the same as using a humanPerformer construct as defined above.

    这完全和上述above定义的humanPerformer 构件相同。

  • candidateUsers attribute: this custom extension allows to make a user a candidate for a task.

    候选用户属性(candidateUsers attribute):这个定制扩展允许让用户成为一个任务的候选人)。

    <userTask id="theTask" name="my task" activiti:candidateUsers="kermit, gonzo" />

    This is exactly the same as using a potentialOwner construct as defined above. Note that it is not required to use the user(kermit) declaration as is the case with thepotential owner construct, since the attribute can only be used for users.

    这个完全和上述above定义的potentialOwner构件相同。注意:因为这个属性只针对用户才能够使用,在带有 potential owner 构件的情况下,并不需要使用user(kermit) 声明。

  • candidateGroups attribute: this custom extension allows to make a group a candidate for a task.

    候选组属性candidateGroups attribute):这个定制扩展允许让一个组成为一个任务的候选人。

    <userTask id="theTask" name="my task" activiti:candidateGroups="management, accountancy" />

    This is exactly the same as using a potentialOwner construct as defined above. Note that it is not required to use the group(management) declaration as is the case with the potential owner construct, since the attribute can only be used for groups.

    这完全和上述above定义采用一个potentialOwner构件方式相同。注意:因为这个属性只针对组才能够使用,在带有 potential owner 构件的情况下,并不需要使用group(management) 声明。

  • candidateUsers and candidateGroups can both be defined on the same user task.

    在同一用户任务之上能够定义candidateUserscandidateGroups两者。

In case the previous approaches are not sufficient, it is possible to delegate to custom assignment logic using a task listener on the create event:

万一前面的方法不够用,在建立事件时使用一个任务监听器 task listener将委托给定制分配逻辑。

<userTask id="task1" name="My task" >
  <extensionElements>
    <activiti:taskListener event="create" class="org.activiti.MyAssignmentHandler" />
  </extensionElements>
</userTask>

The DelegateTask that is passed to the TaskListener implementation, allows to set the assignee and candidate-users/groups:

传递给TaskListener 实现的委托任务DelegateTask,允许设置责任人和候选的用户/组:

public class MyAssignmentHandler implements TaskListener {

  public void notify(DelegateTask delegateTask) {
    // Execute custom identity lookups here
    
    // and then for example call following methods:
    delegateTask.setAssignee("kermit");
    delegateTask.addCandidateUser("fozzie");
    delegateTask.addCandidateGroup("management");
    ...
  }
  
}

When using Spring it is possible to use the custom assignment attirbutes as described in the section above, and delegate to a Spring bean using a task listener with anexpression that listens to task create events. In the following example, the assignee will be set by calling the findManagerOfEmployee on the ldapService Spring bean. The empparameter that is passed, is a process variable>.

当使用Spring时,可能使用上面章节描述的定制分配属性,使用带有一个表达式expression的任务监听器task listener 委托给Spring bean。监听器监听任务建立(create)事件。下例,通过在 Spring bean ldapService上调用findManagerOfEmployee 将设置责任人。传递的emp参数是一个流程变量。

<userTask id="task" name="My Task" activiti:assignee="${ldapService.findManagerForEmployee(emp)}"/>

This also works similar for candidate users and groups:

这也和针对候选用户和组工作相似:

<userTask id="task" name="My Task" activiti:candidateUsers="${ldapService.findAllSales()}"/>

Note that this will only work if the return type of the invoked methods is String or Collection<String> (for candidate users and groups):

注意如果调用方法的返回类型是StringCollection<String>(针对候选用户和组),只有这样才将工作。

            
public class FakeLdapService {
  
  public String findManagerForEmployee(String employee) {
    return "Kermit The Frog";
  }
  
  public List<String> findAllSales() {
    return Arrays.asList("kermit", "gonzo", "fozzie");
  }

}
Script Task(脚本服务)
Description(描述)

A script task is an automatic activity. When a process execution arrives at the script task, the corresponding script is executed.

脚本任务是一个自动化活动。当一个流程执行到达脚本任务时,执行相应的脚本。

Graphical Notation(图形)

A script task is visualized as a typical BPMN 2.0 task (rounded rectangle), with a small 'script' icon in the top-left corner of the rectangle.

d的的脚本任务可视化为一个典型的BPMN 2.0 任务(圆角矩形),在矩形的左上角带有一个小的‘脚本’图标。

bpmn.scripttask

XML representation(

A script task is defined by specifying the script and the scriptFormat.

通过指定脚本script )和脚本格式scriptFormat)定义一个脚本任务。

<scriptTask id="theScriptTask" name="Execute script" scriptFormat="groovy">
  <script>
    sum = 0
    for ( i in inputArray ) {
      sum += i
    }
  </script>
</scriptTask>

The value of the scriptFormat attribute must be a name that is compatible with the JSR-223 (scripting for the Java platform). The Groovy jar is shipped by default with the Activiti distribution. If you want to use another (JSR-223 compatible) scripting engine, it is sufficient to add the corresponding jar to the classpath and use the appropriate name.

scriptFormat属性的值必须是一个和JSR-223 兼容的名称(Java平台上的脚本系统)。发布包缺省带有Groovy jar包。如果你想使用另外的脚本引擎,将相应的jar包加入classpath里。

Variables in scripts(脚本里的变量)

All process variables that are accessible through the execution that arrives in the script task, can be used within the script. In the example, the script variable 'inputArray' is in fact a process variable (an array of integers).

通过到达在脚本任务里的执行,所有可以访问的流程变量也能够在脚本里面使用。在本例中,脚本变量'inputArray'事实上是一个流程变量(一个整数数组)。

<script>
    sum = 0
    for ( i in inputArray ) {
      sum += i
    }
</script>

It's also possible to set process variables in a script, simply by using an assignment statement. In the example above, the 'sum' variable will be stored as a process variable after the script task has been executed. To avoid this behavior, script-local variables can be used. In Groovy, the keyword 'def' must then be used: 'def sum = 0'. In that case, no process variable will be stored.

简单地通过使用一个赋值语句,也可能在一个脚本里设置流程变量。在上例,在脚本任务已经执行之后, 'sum'变量将保存为一个流程变量。为了避免这个行为,能够使用脚本局部变量。在Groovy里面,必须使用关键字 'def''def sum = 0'。在那种情况下,将不保存任何流程变量。

An alternative is to set variables through the current execution, which is available as a reserved variable called 'execution'.

通过当前执行,有另外设置变量的可选方式,作为叫做 'execution'的保留变量可以获得。

<script>
    def scriptVar = "test123"
    execution.setVariable("myVar", scriptVar)
</script>

Note: the following names are reserved and cannot be used as variable names: out, out:print, lang:import, context, elcontext.

注意:下列名称将要保留并且不能作为变量名被使用(cannot be used):out, out:print, lang:import, context, elcontext

Script results(脚本结果)

The return value of a script task can be assigned to an already existing or to a new process variable by specifying the process variable name as a literal value for the'activiti:resultVariableName' attribute of a script task definition. Any existing value for a specific process variable will be overwritten by the result value of the script execution. When not specifying a result variable name, the script result value gets ignored.

为了一个脚本任务定义的属性 'activiti:resultVariableName' ,通过指定流程变量名称为一个字面值,脚本任务的返回值能够分配给已经存在的或者一个新的流程变量。对于一个特定的流程变量的任何存在的值将被脚本执行的结果值复写。当没有指定一个结果变量值,脚本结果值被忽视。

<scriptTask id="theScriptTask" name="Execute script" scriptFormat="juel" activiti:resultVariableName="myVar">
  <script>#{echo}</script>
</scriptTask>

In the above example, the result of the script execution (the value of the resolved expression '#{echo}') is set to the process variable named 'myVar' after the script completes.

的在上例里,在脚本完成之后,脚本执行(解析变量 '#{echo}'的值)的结果被设置为名叫'myVar'的流程变量。

Java Service Task(Java服务任务)
Description(描述)

A Java service task is used to invoke an external Java class.

Java服务用来调用一个外部Java类。

Graphical Notation(图形符号)

A service task is visualized as a rounded rectangle with a small gear icon in the top-left corner.

服务任务可视化为在左上方带有一个小齿轮图标的圆角矩形。

bpmn.java.service.task

XML representation(XML表示)

There are 4 ways of declaring how to invoke Java logic:

声明如何调用Java逻辑有四种方式:

  • Specifying a class that implements JavaDelegate or ActivityBehavior

    指定实现JavaDelegate或者ActivityBehavior的Java类。

  • Evaluating an expression that resolves to a delegation object

    估计解析为一个委托对象的表达式。

  • Invoking a method expression

    调用一个方法表达式。

  • Evaluating a value expression

    估计一个值表达式。

To specify a class that is called during process execution, the fully qualified classname needs to be provided by the 'activiti:class' attribute.

为了指定在流程执行期间的一个类,需要通过'activiti:class' 属性提供全部规范化的类名。

<serviceTask id="javaService" 
             name="My Java Service Task" 
             activiti:class="org.activiti.MyJavaDelegate" />

See the implementation section for more details on how to use such a class.

为了了解如何使用如此这样的类,请参见the implementation section

It is also possible to use an expression that resolves to an object. This object must follow the same rules as objects that are created when the activiti:class attribute is used (seefurther).

也可能使用解析为一个对象的表达式。当使用activiti:class属性时,这个对象必须遵从所建立对象相同的规则(参见 further)。

 <serviceTask id="serviceTask" activiti:delegateExpression="${delegateExpressionBean}" />

Here, the delegateExpressionBean is a bean that implements the JavaDelegate interface, defined in for example the Spring container.

To specify a UEL method expression that should be evaluated, use attribute activiti:expression.

这里,delegateExpressionBean 是一个实现接口JavaDelegate 的bean,例如,在Spring容器里面定义。为了指定一个应当被估计的UEL方法表达式,使用属性activiti:expression

<serviceTask id="javaService" 
             name="My Java Service Task" 
             activiti:expression="#{printer.printMessage()}" />

Method printMessage (without parameters) will be called on the named object called printer.

方法printMessage(没有参数)将在一个名叫 printer的对象上调用。

It's also possible to pass parameters with an method used in the expression.

在表达式里面传递一个参数给方法也是可能的。

<serviceTask id="javaService" 
             name="My Java Service Task" 
             activiti:expression="#{printer.printMessage(execution, myVar)}" />

Method printMessage will be called on the object named printer. The first parameter passed is the, which is available in the expression context by default available as. The second parameter passed, is the value of the variable with name myVar in the current execution.

将在名叫 printer 的对象之上调用方法printMessage 。传递的第一个参数是 DelegateExecution,通过作为 expression的缺省的变量,它在表达式上下文获得。第二个传递的参数,是在当前执行的名叫myVar 的变量值。

To specify a UEL value expression that should be evaluated, use attribute activiti:expression.

为了指定一个应当估计的UEL值表达式,使用属性activiti:expression

<serviceTask id="javaService" 
             name="My Java Service Task" 
             activiti:expression="#{split.ready}" />

The getter method of property ready, getReady (without parameters), will be called on the named bean called split. The named objects are resolved in the execution's process variables and (if applicable) in the Spring context.

属性ready, getReady (没有参数)的gtter方法,将被名叫 split的方法之上调用。可在执行的流程变量和在Spring上下文(如果可应用)解析命名对象。

Implementation(实现)

To implement a class that can be called during process execution, this class needs to implement the org.activiti.engine.delegate.JavaDelegate interface and provide the required logic in the execute method. When process execution arrives at this particular step, it will execute this logic defined in that method and leave the activity in the default BPMN 2.0 way.

在流程执行期间,为了实现一个能够被调用的类,这个类需要实现 org.activiti.engine.delegate.JavaDelegate 接口并在方法里面提供所需的逻辑。当流程执行到达这个特别的步骤时,它将执行在那个方法定义的逻辑,并以缺省BPMN 2.0方式离开那个活动。

[EXPERIMENTAL] It is also possible to provide a class that implements the org.activiti.engine.impl.pvm.delegate.ActivityBehavior interface. Implementations have then access to the more powerful ActivityExecution that for example also allows to influence the control flow of the process. Note however that this is not a very good practice, and should be avoided as much as possible. So, it is advised to use the ActivityBehavior interface only for advanced use cases and if you know exactly what you're doing.

[EXPERIMENTAL] 也有可能提供一个实现 org.activiti.engine.impl.pvm.delegate.ActivityBehavior接口的类然后实现具有访问更加强大的ActivityExecution ,例如允许影响流程的控制流程。但是,注意这不是一个非常好的实践,并且尽可能避免。所以,建议只在高级用例的情况下使用ActivityBehavior 接口,并且你完全知道正在做什么。

Let's create for example a Java class that can be used to change a process variable String to uppercase. This class needs to implement theorg.activiti.engine.delegate.JavaDelegate interface, which requires us to implement the execute(DelegateExecution) method. It's this operation that will be called by the engine and which needs to contain the business logic. Process instance information such as process variables and other can be accessed and manipulated through theDelegateExecution interface (click on the link for a detailed Javadoc of its operations).

例如,让我们建立能够用来改变流程变量字符串为大写的Java类。这个类需要实现org.activiti.engine.delegate.JavaDelegate接口,它需要我们实现 execute(DelegateExecution) 方法。正是这个操作将被引擎调用,并且它需要包含业务逻辑。通过http://activiti.org/javadocs/org/activiti/engine/delegate/DelegateExecution.html接口,诸如流程变量及其他的流程实例信息能够被访问和操纵。

public class ToUppercase implements JavaDelegate {
  
  public void execute(DelegateExecution execution) throws Exception {
    String var = (String) execution.getVariable("input");
    var = var.toUpperCase();
    execution.setVariable("input", var);
  }
  
}

Note: there will be only one instance of that Java class created for the serviceTask it is defined on. All process-instances share the same class instance that will be used to call execute(DelegateExecution). This means that the class must not use any member variables and must be thread-safe, since it can be executed simultaneously from different threads. This also influences the way Field injection is handled.

注意:为serviceTask建立的Java类将只有一个实例。所有流程实例共享将用来调用 execute(DelegateExecution)的相同的类实例。这意味著这个类一定不要使用任何成员变量,并且必须是线程安全的,这是因为能从不同线程同时执行它。这也影响被处理的字段注入的方式 Field injection

The classes that are referenced in the process definition (ie by using activiti:class) are NOT instantiated during deployment. Only when a process execution arrives for the first time at the point in the process where the class is used, an instance of that class will be created. If the class cannot be found, an ActivitiException will be thrown. The reasoning for this is that the environment (and more specifically the classpath) when you are deploying is often different from the actual runtime environment. For example when usingant or the business archive upload in Activiti Probe to deploy processes, the classpath does not contain the referenced classes.

在流程定义里引用的类(即通过使用activiti:class在部署时不被实例化。只有一个流程执行在第一次到达那个类使用的流程某个点时,才将建立那个类的实例。如果不能找到那个类,将抛出ActivitiException 。导致这个异常的原因是你部署的环境(更特别是classpath)经常和实际的运行时环境不同。例如,当使用ant 或者上载业务档案至Activiti Probe来部署流程时,classpath没有包含被引用的类。

Field injection(字段注入)

It's possible to inject values into the fields of the delegated classes. The following types of injection are supported:

将值注入到委托类的字段是可能的。支持的注入类型如下:

  • Fixed string values

    固定字符串值

  • Expressions

    表达式

The following code snippet shows how to inject a constant value into a field. Field injection is supported when using the 'class' attribute. Note that we need to declare a 'extensionElements' XML element before the actual field injection declarations, which is a requirement of the BPMN 2.0 XML Schema.

下列代码片段展示了如何注入一个常量值至一个字段。当使用 'class'属性时,支持字段注入。注意:在实际字段注入声明之前,我们需要声明一个 'extensionElements' XML 元素,这是BPMN 2.0 XML Schema的需求。

<serviceTask id="javaService" 
    name="Java service invocation" 
    activiti:class="org.activiti.examples.bpmn.servicetask.ToUpperCaseFieldInjected">
    <extensionElements> <activiti:field name="text" stringValue="Hello World" /> </extensionElements>           
</serviceTask>        
          

The class ToUpperCaseFieldInjected has a field text which is of type org.activiti.engine.impl.el.Expression. When calling text.getValue(execution), the configured string value Hello World will be returned.

ToUpperCaseFieldInjected 具有是类型org.activiti.engine.impl.el.Expression的字段。当调用text.getValue(execution)时,将返回已配置的字符串值 Hello World

Alternatively, for longs texts (e.g. an inline e-mail) the 'activiti:string' sub element can be used:

另外,对于长文本(例如一个内嵌的e-mail),能够使用 'activiti:string' 子元素。

<serviceTask id="javaService" 
    name="Java service invocation" 
    activiti:class="org.activiti.examples.bpmn.servicetask.ToUpperCaseFieldInjected">
  <extensionElements>   
    <activiti:field name="text">
        <activiti:string> Hello World </activiti:string>
    </activiti:field>
  </extensionElements>        
</serviceTask>        
          

To inject values that are dynamically resolved at runtime, expressions can be used. Those expressions can use process variables, or Spring defined beans (if Spring is used). As noted in Service Task Implementation, an instance of the Java class is shared among all process-instances in a service task. To have dynamic injection of values in fields, you can inject value and method expressions in a org.activiti.engine.impl.el.Expression which can be evaluated/invoked using the DelegateExecution passed in the execute method.

为了注入在运行时动态解析的值,能够使用表达式。那些表达式能够使用流程变量,或者Spring定义bean(如果使用Spring)。正如 Service Task Implementation所述,Java类的实例在一个服务任务里在所有流程实例间共享。为了在字段里具有值的动态注入,你能够注入值和在org.activiti.engine.impl.el.Expression里面的方法表达式。通过使用在方法传递参数,能够估计和调用这个表达式。

<serviceTask id="javaService" name="Java service invocation" 
  activiti:class="org.activiti.examples.bpmn.servicetask.ReverseStringsFieldInjected">
  
  <extensionElements>
    <activiti:field name="text1">
      <activiti:expression>${genderBean.getGenderString(gender)}</activiti:expression>
    </activiti:field>
    <activiti:field name="text2">
       <activiti:expression>Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}</activiti:expression>
    </activiti:field>
  </ extensionElements>
</ serviceTask>

The example class below uses the injected expressions and resolves them using the current DelegateExecution. Full code and test can be found inorg.activiti.examples.bpmn.servicetask.JavaServiceTaskTest.testExpressionFieldInjection

下面示例类使用注入表达式并使用当前DelegateExecutio解析它们。所有的源代码在org.activiti.examples.bpmn.servicetask.JavaServiceTaskTest.testExpressionFieldInjection里找到。

public class ReverseStringsFieldInjected implements JavaDelegate {

  private Expression text1;
  private Expression text2;

  public void execute(DelegateExecution execution) {
    String value1 = (String) text1.invoke(execution);
    execution.setVariable("var1", new StringBuffer(value1).reverse().toString());

    String value2 = (String) text2.getValue(execution);
    execution.setVariable("var2", new StringBuffer(value2).reverse().toString());
  }
}

Alternatively, you can also set the expressions as an attribute instead of a child-element, to make the XML less verbose.

另外,你也能设置表达式为一个属性代替子元素,让XML 少一些啰嗦信息。

<activiti:field name="text1" expression="${genderBean.getGenderString(gender)}" />
<activiti:field name="text1" expression="Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}" />

Since the Java class instance is reused, the injection only happens once, when the serviceTask is called the first time. When the fields are altered by your code, the values won't be re-injected so you should treat them as immutable and don't make any changes to them.

因为复用Java类实例,所有注入只发生一次,当第一次调用serviceTask时发生。当代码改变这个字段,这个值不会重新注入。所以,你应当把它们视为不可变的,并且不要让它们发生变化

Service task results(服务任务结果)

The return value of a service execution (for service task using expression only) can be assigned to an already existing or to a new process variable by specifying the process variable name as a literal value for the 'activiti:resultVariableName' attribute of a service task definition. Any existing value for a specific process variable will be overwritten by the result value of the service execution. When not specifying a result variable name, the service execution result value gets ignored.

通过为服务任务定义的'activiti:resultVariableName' 属性指定流程变量名为字面量,服务执行的返回值(针对服务任务只使用表达式)能够被分配给的的一个已经存在的或者一个新的流程变量。针对一个特别的流程变量的存在值将被服务执行的结果值改写。当没指定一个结果变量名称,将忽略服务执行结果值。

<serviceTask id="aMethodExpressionServiceTask"
    activiti:expression="#{myService.doSomething()}"
    activiti:resultVariableName="myVar" />

In the example above, the result of the service execution (the return value of the 'doSomething()' method invocation on an object that is made available under the name'myService' either in the process variables or as a Spring bean) is set to the process variable named 'myVar' after the service execution completes.

在上例,在服务执行之后,服务执行的结果(可以获得在一个对象之上名叫 'myService' 之下的'doSomething()' 方法调用的返回值。这个返回值或者是流程变量,或者作为一个Spring bean)设置为名叫'myVar' 的流程变量。

Execution listener(执行监听)

Execution listeners allow you to execute external Java code or evaluate an expression when certain events occur during process exevcution. The events that can be captured are:

在流程执行期间,当某一事件发生,执行监听器允许执行外部Java代码或者估计一个表达式。能够捕获的事件为:

  • Start and ending of a process instance.

    启动和终止一个流程实例。

  • Taking a transition.

    执行一个过渡。

  • Start and ending of an activity.

    启动和终止一个活动。

The following process definition contains 3 execution listenerss:

下列流程定义包含3个执行监听器:

  <process id="executionListenersProcess">
  
    <extensionElements> <activiti:executionListener class="org.activiti.examples.bpmn.executionlistener.ExampleExecutionListenerOne" event="start" /> </extensionElements>
    
    <startEvent id="theStart" />
    <sequenceFlow sourceRef="theStart" targetRef="firstTask" />
    
    <userTask id="firstTask" />
    <sequenceFlow sourceRef="firstTask" targetRef="secondTask">
    <extensionElements> <activiti:executionListener class="org.activiti.examples.bpmn.executionListener.ExampleExecutionListenerTwo" /> </extensionElements>
    </sequenceFlow>
    
    <userTask id="secondTask" >
    <extensionElements> <activiti:executionListener expression="${myPojo.myMethod(execution.event)}" event="end" /> </extensionElements>
    </userTask>
    <sequenceFlow sourceRef="secondTask" targetRef="thirdTask" />
       
    <userTask id="thirdTask" />
    <sequenceFlow sourceRef="thirdTask" targetRef="theEnd" />

    <endEvent id="theEnd" />
    
  </process>

The first execution listener is notified when the process starts. The listener is an external Java-class (like ExampleExecutionListenerOne) and should implementorg.activiti.engine.impl.pvm.delegate.ExecutionListener interface. When the event occurs (in this case end event) the method notify(ExecutionListenerExecution execution) is called.

当流程启动时,首先通知第一个执行监听器。监听器是个外部类(像ExampleExecutionListenerOne)并应当实现org.activiti.engine.impl.pvm.delegate.ExecutionListener 接口。当事件发生(本例为结束事件)时,调用notify(ExecutionListenerExecution execution) 方法。

public class ExampleExecutionListenerOne implements ExecutionListener {

  public void notify(DelegateExecution execution) throws Exception {
    execution.setVariable("variableSetInExecutionListener", "firstValue");
    execution.setVariable("eventReceived", execution.getevent());
  }
}

It is also possible to use a delegation class that implements the org.activiti.engine.delegate.JavaDelegate interface. These delegation classes can then be reused in other constructs, such as a delegation for a serviceTask.

也有可能使用一个实现org.activiti.engine.delegate.JavaDelegate接口的委托类。这些委托类能够在其它构件里复用,比如serviceTask的委托。

The second execution listener is called when the transition is taken. Note that the listener element doesn't define an event, since only take events are fired on transitions. Values in the event attribute are ignored when a listener is defined on a transition.

当过渡发生时,调用第二个执行监听器。注意元素不定义event,因为只取得在过渡时发生的事件。当在过渡之上定义一个监听器是,忽略event 属性的值

The last execution listener is called when activity secondTask ends. Instead of using the class on the listener declaration, a expression is defined instead which is evaluated/invoked when the event is fired.

当活动secondTask结束时,调用最后那个执行监听器。代替在监听器声明上使用 class,当事件激活时,定义一个被估计/调用的expression

<activiti:executionListener expression="${myPojo.myMethod(execution.eventName)}" event="end" />

As with other expressions, execution variables are resolved and can be used. Because the execution implementation object has a property that exposes the event name, it's possible to pass the event-name to your methods using execution.eventName.

正如其它表达式,执行变量被解析并能够使用。因为执行实现对象具有暴露事件名的属性,所以使用execution.eventName传递事件名给方法是可能的。

Execution listeners also support using a delegateExpression, similar to a service task.

执行监听器也支持使用 delegateExpression,和服务任务相似similar to a service task

<activiti:executionListener event="start" delegateExpression="${myExecutionListenerBean}" />
Field injection on execution listeners(在执行监听器上进行字段注入)

When using an execution listener that is configured with the class attribute, field injection can be applied. This is exactly the same mechanism as used Service task field injection, which contains an overview of the possibilities provided by field injection.

当使用以属性配置的一个执行监听器,能够使用字段注入。这完全和所用的服务任务字段注入Service task field injection相同的机制,它包含了由字段注入提供的可能性概观。

The fragment below shows a simple example process with an execution listener with fields injected.

下面的片段采用字段注入,使用一个执行监听器,展示了一个简单的示例流程。

 <process id="executionListenersProcess">
    <extensionElements> <activiti:executionListener class="org.activiti.examples.bpmn.executionListener.ExampleFieldInjectedExecutionListener" event="start"> <activiti:field name="fixedValue" stringValue="Yes, I am " /> <activiti:field name="dynamicValue" expression="${myVar}" /> </activiti:executionListener> </extensionElements>
    
    <startEvent id="theStart" />
    <sequenceFlow sourceRef="theStart" targetRef="firstTask" />
    
    <userTask id="firstTask" />
    <sequenceFlow sourceRef="firstTask" targetRef="theEnd" />
    
    <endEvent id="theEnd" />
  </process>
        
public class ExampleFieldInjectedExecutionListener implements ExecutionListener {

  private Expression fixedValue; private Expression dynamicValue;

  public void notify(ExecutionListenerExecution execution) throws Exception {
    execution.setVariable("var", fixedValue.getValue(execution).toString() + dynamicValue.getValue(execution).toString());
  }
}
          

The class ExampleFieldInjectedExecutionListener concatenates the 2 injected fields (one fixed an the other dynamic) and stores this in the process variable 'var'.

类ExampleFieldInjectedExecutionListener串连2个注入字段(一个固定,另外一个动态),并将这保存在流程变量'var'里面。

@Deployment(resources = {"org/activiti/examples/bpmn/executionListener/ExecutionListenersFieldInjectionProcess.bpmn20.xml"})
public void testExecutionListenerFieldInjection() {
  Map<String, Object> variables = new HashMap<String, Object>();
  variables.put("myVar", "listening!");
    
  ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("executionListenersProcess", variables);
    
  Object varSetByListener = runtimeService.getVariable(processInstance.getId(), "var");
  assertNotNull(varSetByListener);
  assertTrue(varSetByListener instanceof String);
    
  // Result is a concatenation of fixed injected field and injected expression
  assertEquals("Yes, I am listening!", varSetByListener);
}
        
Task listener(任务监听器)

A task listener is used to execute custom Java logic or an expression upon the occurrence of a certain task-related event.

任务监听器用来执行定制的Java逻辑或者某一任务相关事件发生的一个表达式。

A task listener can only be added in the process definition as a child element of a user task. Note that this also must happen as a child of the BPMN 2.0 extensionElementsand in the activiti namespace, since a task listener is an Activiti-specific construct.

一个任务监听器只能够作为一个用户任务user task的子元素添加至流程定义里。注意这也必须是BPMN 2.0 extensionElements的子元素,并在 activiti 命名空间里,因为任务监听器是一个Activiti特定的构件。

<userTask id="myTask" name="My Task" >
  <extensionElements>
    <activiti:taskListener event="create" class="org.activiti.MyTaskCreateListener" />
  </extensionElements>
</userTask>

A task listener supports following attributes:

任务监听器支持下列的属性:

  • event (required): the type of task event on which the task listener will be invoked. Possible values are 'create' (occurs when the task has been created an all task properties are set), 'assignment' (occurs when the task is assigned to somebody) or 'complete' (occurs when the task is completed and just before the task is deleted from the runtime data).

    事件(必需):任务监听器将要调用的任务事件的类型。可能的值是建立 'create'(当任务已经建立,所有特性被设置),分配'assignment'(当任务被分配给某人),或者完成'complete'(当任务完成时发生,正是式在任务从运行时数据之前)

  • class: the delegation class that must be called. This class must implement the interface.

    类:必须调用的委托类。这个类必须实现org.activiti.engine.impl.pvm.delegate.TaskListener 接口。

    public class MyTaskCreateListener implements TaskListener {
    
      public void notify(DelegateTask delegateTask) {
        // Custom logic goes here
      }
    
    }

    It is also possible to use field injection to pass process variables or the execution to the delegation class. Note that an instance of the delegation class is created upon process deployment (as is the case with any class delegation in Activiti), which means that the instance is shared between all process instance executions.

    可能使用字段注入 field injection来传递流程变量或者委托类的执行。注意一旦流程部署,将建立一个委托的实例(正如在Activiti里的任何类委托的情形),它意味着在所有流程实例执行之间共享实例。

  • expression: (cannot be used together with the class attribute): specifies an expression that will be executed when the event happens.

    表达式(expression:(不能和class 属性一起使用):当事件发生时执行将要被执行的表达式。

    <activiti:taskListener event="create" expression="${execution.setVariable('myVar', 'Hello from the task listener!')}" />
  • delegateExpression allows to specify an expression that resolves to an object implementing ther TaskListener interface, similar to a service task.

    delegateExpression 允许指定一个解析为一个实现TaskListener接口的对象的表达式,和服务任务相似 similar to a service task.

    <activiti:taskListener event="create" delegateExpression="${myTaskListenerBean}" />
Email task(Email 任务)

Activiti allows to enhance business processes with automatic mail service tasks that send e-mails to one or more recipients, including support for cc, bcc, html content, ... etc. Note that the mail task is not an 'official' task of the BPMN 2.0 spec (and it does not have a dedicated icon as a consequence). Hence, in Activiti the mail task is implemented as a dedicated service task.

Activiti用自动化的邮件服务任务来加强业务流程。这些邮件服务包括给一个或多个接收人发送电子邮件,提供对cc,bcc,html内容。。。等等的支持。注意邮件服务不是一个BPMN 2.0规范的官方任务(并且作为结果没有专有图标)。由此,在Activiti里面,邮件服务作为一个专有的服务任务而实现。

Mail server configuration(邮件服务器配置)

The Activiti engine sends e-mails trough an external mail server with SMTP capabilities. To actually send e-mails, the engine needs to know how to reach the mail server. Following properties can be set in the activiti.cfg.xml configuration file:

Activiti引擎通过一个外部具有SMTP能力的邮件服务器发送电子邮件。事实上,为了发送电子邮件,引擎需要知道如何连接邮件服务器。在配置文件activiti.cfg.xml 里能够设置下列属性:

Table 1.1. Mail server configuration

Property
Required?
Description

mailServerHost
no
The hostname of your mail server (e.g. mail.mycorp.com). Default is localhost

mailServerPort
yes, if not on the default port
The port for SMTP traffic on the mail server. The default is 25

mailServerDefaultFrom
no
The default e-mail address of the sender of e-mails, when none is provided by the user. By default this isactiviti@activiti.org

mailServerUsername
if applicable for your server
Some mail servers require credentials for sending e-mail. Default is activiti.

mailServerPassword
if applicable for your server
Some mail servers require credentials for sending e-mail. Default is activiti.

Defining an Email Task(定义一个Email任务)

The Email task is implemented as a dedicated Service Task and is defined by setting 'mail' for the type of the service task.

Email 任务是作为一个专门的服务任务而实现的,通过将服务任务的类型设置为'mail' 而定义。

<serviceTask id="sendMail" activiti:type="mail">        
        

The Email task is configured by field injection. All the values for these properties can contain EL expression, which are resolved at runtime during process execution. Following properties can be set:

通过字段注入 field injection配置邮件任务。这些属性的所有值能包含EL表达式,在流程执行期间,在运行时它被解析。能够设置下列属性:

Table 1.2. Mail server configuration

Property
Required?
Description

to
yes
The recipients if the e-mail. Multiple recipients are defined in a comma-separated list

from
no
The sender e-mail address. If not provided, the default configured from address is used.

subject
no
The subject of the e-mail.

cc
no
The cc's of the e-mail. Multiple recipients are defined in a comma-separated list

bcc
no
The bcc's of the e-mail. Multiple recipients are defined in a comma-separated list

html
no
A piece of HTML that is the content of the e-mail.

text
no
The content of the e-mail, in case one needs to send plain none-rich e-mails. Can be used in combination with html, for e-mail clients that don't support rich content. The client will then fall back to this text-only alternative.

Example usage(使用示例)

The following XML snippet shows an example of using the Email Task.

下列XML片段展示了使用Email任务的一个示例。

<serviceTask id="sendMail" activiti:type="mail">
  <extensionElements>
    <activiti:field name="from" stringValue="order-shipping@thecompany.com" />
    <activiti:field name="to" expression="${recipient}" />
    <activiti:field name="subject" expression="Your order ${orderId} has been shipped" />
    <activiti:field name="html">
      <activiti:expression>
        <![CDATA[
          <html>
            <body>
              Hello ${male ? 'Mr.' : 'Mrs.' } ${recipientName},<br/><br/>
                 
              As of ${now}, your order has been <b>processed and shipped</b>.<br/><br/>
                  
              Kind regards,<br/>
                  
              TheCompany.
            </body>
          </html>
        ]]>
      </activiti:expression>
    </activiti:field>      
  </extensionElements>
</serviceTask>        
        

with the following result:

结果如下:

email.task.result

Manual Task(手动任务)
Description(描述)

A Manual Task defines a task that is external to the BPM engine. It is used to model work that is done by somebody, which the engine does not need to know of, nor is there a system or UI interface. For the engine, a manual task is handled as a pass-through activity, automatically continuing the process from the moment process execution arrives into it.

一个手动任务定义了BPM引擎之外的一个任务。用它来对由某人所完成的工作建模,引擎并不需要知道,既不存在系统,也没有UI接口。对于引擎,一个手动任务作为一个传递活动(pass-through activity处理。 从流程执行达到那个时刻,自动继续那个流程。

Graphical Notation(图形符号)

A manual task is visualized as a rounded rectangle, with a little 'hand' icon in the upper left corner

手动任务可视化为一个圆角矩形,在左上角带有一个手型图标。

bpmn.manual.task

XML representation(XML表示)
<manualTask id="myManualTask" name="Call client for more information" />
Java receive task(Java接受任务)
Description(描述)

A receive task is a simple task that waits for the arrival of a certain message. Currently, we have only implemented Java semantics for this task. When process execution arrives at a receive task, the process state is committed to the persistence store. This means that the process will stay in this wait state, until a specific message is received by the engine, which triggers the continuation of the process past the receive task.

接受任务是个等待某一消息到达的简单任务。当前,我们为这个任务只实现了Java语义。当流程执行到达一个接收任务时,流程状态被提交到持久化存储里面。这意味着停留在等待状态,直到引擎收到一个特别消息,它将触发通过接收任务的流程延续。

Graphical notation(图形符号)

A receive task is visualized as a task (rounded rectangle) with a message icon in the top left corner. The message is white (a black message icon would have send semantics)

接受任务可视化为左上角带有一个消息图标的任务(圆角矩形),消息是白色(黑色消息将有发送语义)。

bpmn.receive.task

XML representation
<receiveTask id="waitState" name="wait" />    

To continue a process instance that is currently waiting at such a receive task, the runtimeService.signal(executionId) must be called using the id of the execution that arrived in the receive task. The following code snippet shows how this works in practice:

为了继续当前正在一个接收任务的流程实例,通过使用到达接收任务里执行的id,必须使用 runtimeService.signal(executionId) 。下列代码片段展示了实践上这个工作是任何进行的:

ProcessInstance pi = runtimeService.startProcessInstanceByKey("receiveTask");
Execution execution = runtimeService.createExecutionQuery()
  .processInstanceId(pi.getId())
  .activityId("wait")
  .singleResult();
assertNotNull(execution);
    
runtimeService.signal(execution.getId());   
          
Boundary events(边界事件)

Boundary events are catching events that are attached to an activity (a boundary event can never be throwing). This means that while the activity is running, the event islistening for a certain type of trigger. When the event is caught, the activity is interrupted and the sequence flow going out of the event are followed.

边界事件是能附属在一个活动的捕获事件(catching )(从不抛出边界事件)。这意味著当活动运行时,事件总是监听某个类型的触发器。当捕获到事件时,活动被中断,可以跟随一个顺序流。

All boundary events are defined in the same way:

所有边界事件用相同方式定义:

<boundaryEvent id="myBoundaryEvent" attachedToRef="theActivity">
      <XXXEventDefinition/>
</boundaryEvent>

A boundary event is defined with

边界事件以下面方式来定义

  • A unique identifier (process-wide)

    唯一标识符(整个流程范围)

  • A reference to the activity to which the event is attached through the attachedToRef attribute. Note that a boundary event is defined on the same level as the activities to which they are attached (i.e. no inclusion of the boundary event inside the activity).

    通过attachedToRef 属性,引用到那个事件的活动。注意边界事件和它所附属的活动在一个层次(即,在活动中也可不包括边界事件)。

  • An XML sub-element of the form XXXEventDefinition (e.g. TimerEventDefinition, ErrorEventDefinition, etc.) defining the type of the boundary event. See the specific boundary event types for more details.

    表单( XXXEventDefinition)(例如TimerEventDefinition, ErrorEventDefinition,等等)的XML子元素定义边界事件的类型。详情参见 特别的事件类型。

Timer Boundary Event(定时器边界事件)
Description(描述)

A timer boundary event acts as a stopwatch and alarm clock. When an execution arrives in the activity where the boundary event is attached to, a timer is started. When the timer fires (e.g. after a specified interval), the activity is interrupted and the sequence flow going out of the timer boundary event are followed.

定时器边界事件扮演一个跑表和报警时钟。当一个执行到达边界事件的的的的的的的

Graphical Notation(图形符号)

A timer boundary event is visualized as a typical boundary event (i.e. circle on the border), with the timer icon on the inside.

一个定时器边界事件可视化为一个典型的边界事件(即,在边上的圆圈),里面带有一个定时器图标。

bpmn.boundary.timer.event

XML Representation(XML表示)

A timer boundary event is defined as a regular boundary event. The specific type sub-element is in this case a timerEventDefinition element.

一个定时器边界事件作为一个正规的边界事件定义regular boundary event定义。特别类型子元素是timerEventDefinition 元素。

<boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport">
   <timerEventDefinition> <timeDuration>PT4H</timeDuration> </timerEventDefinition>
</boundaryEvent>          

To specify how long the timer should run before it is fired, a timeDuration can be specified as sub-element of timerEventDefinition. The format used is the ISO 8601 format (as required by the BPMN 2.0 specification).

Note: boundary timer events are only fired when the job executor is enabled (i.e. jobExecutorActivate needs to be set to true in the activiti.cfg.xml, since the job executor is disabled by default).

Known issue with boundary events()

There is a known issue regarding concurrency when using boundary events of any type. Currently, it is not possible to have multiple outgoing sequence flow attached to a boundary event (see issue ACT-47). A solution to this problem is to use one outgoing sequence flow that goes to a parallel gateway.

当使用任何类型的边界事件时,存在一个有关并发的已知问题。当前让多个即将离开的顺序流附加到边界事件(参见问题ACT-47)。这个问题的解决方案是使用到达一个并行网关的即将离开的顺序流。

bpmn.known.issue.boundary.event

SubProcess(子流程)
Description(描述)

A subprocess is an activity that contains other activities, gateways, events, etc. which on itself form a process that is part of the bigger process. A subprocess is completely defined inside a parent process (that's why it's often called an embedded subprocess).

子流程( subprocess)是一个包括了其它活动,网关,事件等等的活动。其自身是一个更大流程的部分流程。子流程(subprocess )完全定义在一个父流程里(那就是它为什么经常称为嵌入式(embedded)子流程的原因)。

Subprocesses have two major use cases:

子流程有两个主要的用例:

  • Subprocesses allow hierarchical modeling. Many modeling tools allow that subprocesses can be collapsed, hiding all the details of the subprocess and displaying a high-level end-to-end overview of the business process.

    子流程允许层次建模hierarchical modeling)。许多建模工具允许折叠(collapsed)子流程。折叠隐藏了子流程的细节并显示了高级别的业务流程的端到端的总体概貌。

  • A subprocess creates a new scope for events. Events that are thrown during execution of the subprocess, can be caught by a boundary event on the boundary of the subprocess, thus creating a scope for that event limited to the subprocess.

    子流程建立一个新的事件范围(scope for events.)。在子流程的执行期间抛出事件。这个事件被子流程边界上的边界事件a boundary event 所捕获, 所以为限制到子流程的事件建立一个范围。

Using a subprocess does impose some constraints:

使用子流程一定要强加一些约束:

  • A subprocess can only have one none start event, no other start event types are allowed. A subprocess must at least have one end event. Note that the BPMN 2.0 specification allows to omit the start and end events in a subprocess, but the current Activiti implementation does not support this.

    子流程只能有唯一的none启动事件one none start event),不允许其它的启动事件类型。子流程必须至少有一个结束事件( at least have one end event)。注意尽管BPMN 2.0规范允许忽略子流程的启动和结束事件,但是当前Activiti的实现并不支持这样的功能。

  • Sequence flow can not cross subprocess boundaries.

    顺序流不能跨越子流程边界。

Graphical Notation(图形符号)

A subprocess is visualized as a typical activity, i.e. a rounded rectangle. In case the subprocess is collapsed, only the name and a plus-sign are displayed, giving a high-level overview of the process:

子流程可视化为一个典型的活动,即,一个圆角矩形。如果是子流程被折叠(collapsed)的情况,只显示名称和加号。这样给出了流程高层的总体概况。

bpmn.collapsed.subprocess

In case the subprocess is expanded, the steps of the subprocess are displayed within the subprocess boundaries:

一旦展开( expanded)子流程,子流程的步骤在带有子流程边界里显示。

bpmn.expanded.subprocess

One of the main reasons to use a subprocess, is to define a scope for a certain event. The following process model shows this: both the investigate software/investigate hardware tasks need to be done in parallel, but both tasks need to be done within a certain time, before Level 2 support is consulted. Here, the scope of the timer (i.e. which activities must be done in time) is constrained by the subprocess.

使用子流程的主要原因之一是为某一事件定义一个范围。下列流程模型表明这一点:调研软件和调研硬件 (investigate software/investigate hardware )任务需要并行完成,但是它们都要需在2层支持 (Level 2 support)之前的特定时间内完成。这里,定时器由子流程来约束(例如,流程必须及时完成)。

bpmn.subprocess.with.boundary.timer

XML representation(表示)

A subprocess is defined by the subprocess element. All activities, gateways, events, etc. that are part of the subprocess, need to be enclosed within this element.

子流程由子流程( subprocess)元素定义。所有的活动,网关,事件等等,它们是子流程的部分,被子流程包围。

<subProcess id="subProcess">
    
  <startEvent id="subProcessStart" />
  
  ... other subprocess elements ...

  <endEvent id="subProcessEnd" />
    
 </subProcess>          
          
Call activity (subprocess)(调用活动(子流程))
Description(描述)

BPMN 2.0 makes a distinction between a regular subprocess , often also called embedded subprocess, and the call activity, which looks very similar. From a conceptual point of view, both will call a subprocess when process execution arrives at the activity.

BPMN 2.0严格区分正常子流程 subprocess 和调用活动,它们看起来相似。子流程通常也叫嵌入式子流程( embedded subprocess)。从概念观点来看,当流程执行到达活动时,它们都要调用子流程。

The difference is that the call activity references a process that is external to the process definition, whereas the subprocess is embedded within the original process definition. The main use case for the call activity is to have a reusable process definition that can be called from multiple other process definitions.

区别是调用活动引用到外部流程定义的一个流程,而子流程 subprocess 嵌入到原始流程的里面。调用活动主要的使用情形是复用流程定义。复用流程定义能够被多个其它流程定义调用。

When process execution arrives in the call activity, a new execution is created that is a sub-execution of the execution that arrives in the call activity. This sub-execution is then used to execute the subprocess, potentially creating parallel child execution as within a regular process. The super-execution waits until the subprocess is completely ended, and continues the original process afterwards.

当流程执行到达调用活动( call activity)里时,新建的执行是所到达执行的子执行。然后用这个子执行执行子流程,在一个正常的流程里,潜在地建立并行的子执行。超执行等到所有的子流程完全结束,并随后继续原始的流程。

Graphical Notation(图形符号)

A call activity is visualized the same as a subprocess, however with a thick border (collapsed and expanded). Depending on the modeling tool, a call activity can also be expanded, but the default visualization is the collapsed subprocess representation.

调用活动和子流程subprocess的可视化相同,但是边框要粗些(可以折叠和展开)。依赖于建模工具的不同,一个调用活动可以展开,但是缺省的可视化是表示为已折叠的子流程。

bpmn.collapsed.call.activity

XML representation(XML表示)

A call activity is a regular activity, that requires a calledElement that references a process definition by its key. In practice, this means that the id of the process is used in thecalledElement.

一个调用活动是一个正常的活动,需要一个通过它的Key引用到流程定义的调用元素( calledElement )。在实践上,这意味着流程id( id of the process )在 calledElement里使用。

<callActivity id="callCheckCreditProcess" name="Check credit" calledElement="checkCreditProcess" />

Note that the process definition of the subprocess is resolved at runtime. This means that the subprocess can be deployed independently from the calling process, if needed.

注意子流程的流程定义在运行时解析resolved at runtime.)。这意味着如果需要,子流程能够独立于调用流程发布。

Example(示例)

The following process diagram shows a simple handling of an order. Since the checking of the customer's credit could be common to many other processes, the check credit step is modeled here as a call activity.

下面的流程图展示了一个简单订单的处理。因为顾客信用卡的校验是许多其它流程的公共流程,所以在此将校验信用卡步骤(check credit step )建模为一个调用活动。

bpmn.call.activity.super.process

The process looks as follows:

流程示意如下:

<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="receiveOrder" />

<manualTask id="receiveOrder" name="Receive Order" />
<sequenceFlow id="flow2" sourceRef="receiveOrder" targetRef="callCheckCreditProcess" />
    
<callActivity id="callCheckCreditProcess" name="Check credit" calledElement="checkCreditProcess" />
<sequenceFlow id="flow3" sourceRef="callCheckCreditProcess" targetRef="prepareAndShipTask" />
   
<userTask id="prepareAndShipTask" name="Prepare and Ship" />
<sequenceFlow id="flow4" sourceRef="prepareAndShipTask" targetRef="end" />
    
<endEvent id="end" />

The subprocess looks as follows:

子流程如下所示:

bpmn.call.activity.sub.process

There is nothing special to the process definition of the subprocess. It could as well be used without being called from another process.

子流程并没有特殊的流程定义。它也能不用从其它流程调用而独自使用。


评论

# re: Activiti User Guide(Activiti用户指南)-Chapter 7. BPMN  回复  更多评论   

2010-12-14 21:12 by 网路冷眼@BlogJava
@团购
谢谢光临,望共同探讨Activiti & BPMN!

# re: Activiti User Guide(Activiti用户指南)-Chapter 7. BPMN  回复  更多评论   

2011-07-26 17:14 by iammjj
中文资源太少了,楼主好人~~
请教一个问题,我现在用5.6,想从一个ProcessDefinition中得到这个流程所有的任务,该调用哪个接口?查了很久API没找到
多谢

# re: Activiti User Guide(Activiti用户指南)-Chapter 7. BPMN  回复  更多评论   

2014-06-19 16:16 by cliff
用子流程,如果实例化多个子流程?

只有注册用户登录后才能发表评论。


网站导航: