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結尾,因為否則引擎在部署時不會部署這個文件。
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 元素。在這個元素里,可以定義多個流程定義(盡管我們每個文件只定義一個流程定義,因為開發過程這簡化后面的維護工作)。空的流程定義如下所示。注意最小的定義元素只需xmlns和 targetNamespace 聲明。 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 對象 ProcessDefinition 的key特性。通過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的概念, 最終結果如下所示。
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構件的細節部分):
<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' 連接。
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頁面查看這個新任務。注意盡管是其他人啟動這個任務的,但是這個任務仍將作為換一個候選任務對于會計組內的任何人可見。
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按鈕將調用相同的操作。現在任務將移動到登錄用戶的個人任務里面。
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(他是一個經理)登錄。第二個任務出現在未分配任務列表里面。
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里,你能驗證這個事實,因為流程執行所保存的庫表中找不到任何記錄。
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啟動事件。(即無觸發類型)。
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結束事件用不帶內部圖標的粗線圓圈表示(沒有結果類型)。
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.
順序流可視化為從源元素到目標元素的一個箭頭。箭頭總是指向目標元素。
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.
條件順序流作為一個正常的順序流可視化,開始帶有一個寶石形狀。條件表達式顯示在順序流之上。
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):
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.
網關以一個菱形表示,里面帶有一個圖標。這個圖標顯示了網關的類型。
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。
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:
以下面的模型作為示例:
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'語義。
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里,下列流程是合法的。
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.
用戶任務以一個典型的任務來可視化(圓角矩形),在左上方帶有一個用戶圖標。
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.
在同一用戶任務之上能夠定義candidateUsers 和 candidateGroups兩者。
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):
注意如果調用方法的返回類型是String
或 Collection<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 任務(圓角矩形),在矩形的左上角帶有一個小的‘腳本’圖標。
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.
服務任務可視化為在左上方帶有一個小齒輪圖標的圓角矩形。
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代碼或者估計一個表達式。能夠捕獲的事件為:
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:
結果如下:
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
手動任務可視化為一個圓角矩形,在左上角帶有一個手型圖標。
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)
接受任務可視化為左上角帶有一個消息圖標的任務(圓角矩形),消息是白色(黑色消息將有發送語義)。
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.
一個定時器邊界事件可視化為一個典型的邊界事件(即,在邊上的圓圈),里面帶有一個定時器圖標。
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)。這個問題的解決方案是使用到達一個并行網關的即將離開的順序流。
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)的情況,只顯示名稱和加號。這樣給出了流程高層的總體概況。
In case the subprocess is expanded, the steps of the subprocess are displayed within the subprocess boundaries:
一旦展開( expanded)子流程,子流程的步驟在帶有子流程邊界里顯示。
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)之前的特定時間內完成。這里,定時器由子流程來約束(例如,流程必須及時完成)。
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的可視化相同,但是邊框要粗些(可以折疊和展開)。依賴于建模工具的不同,一個調用活動可以展開,但是缺省的可視化是表示為已折疊的子流程。
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 )建模為一個調用活動。
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:
子流程如下所示:
There is nothing special to the process definition of the subprocess. It could as well be used without being called from another process.
子流程并沒有特殊的流程定義。它也能不用從其它流程調用而獨自使用。