Liferay7 BPM门户开发之2: BPMN 2.0 规范入门 (Activiti BPMN extensions)
Liferay最大的问题是BPM弱,如果做企业开发,BPM必不可少,所以直入主题,做个BPMN2入门.
本文参考地址:http://activiti.org/userguide/index.html#bpmnConstructs
BPMN 2.0中的重要概念:
- Events 事件
- Sequence Flow 顺序流
- Gateways 网关
- Tasks 任务
- Sub-Processes and Call Activities 子流程
- Transactions and Concurrency 事务并发
- Process Initiation Authorization 初始化认证
- Data objects 流程数据
其他相关项:
- Form properties 表单属性
- External form rendering 外部表单集成
1、Events
1、1 Timer Event Definitions
由时间触发的时间,用于
- start event
- intermediate event
- boundary event
必须有确切的一个元素,分别是:
timeDate
<timerEventDefinition> <timeDate>2011-03-11T12:13:14</timeDate> </timerEventDefinition>
在确切的时间点执行
timeDuration
<timerEventDefinition> <timeDuration>P10D</timeDuration> </timerEventDefinition>
从最后一个任务完成后10天开始执行
timeCycle
<timerEventDefinition>
<timeCycle activiti:endDate="2015-02-25T16:42:11+00:00">R3/PT10H</timeCycle>
</timerEventDefinition>
或者变量形式:
<timerEventDefinition>
<timeCycle>R3/PT10H/${EndDate}</timeCycle>
</timerEventDefinition>
循环3次,间隔10小时
也可以使用cron expressions :http://www.quartz-scheduler.org/documentation/
1.2 Signal Event Definitions
一个例子:https://github.com/chanjarster/activiti-learn/wiki/%E8%AF%A6%E8%A7%A3signal%20event
1.3 Message Event Definitions
想象一下,作为一个嵌入式的流程引擎(不是国内很多固化Hardcode式的流程引擎),Activiti关心的是实际从第三方应用系统接收的消息。这将是环境依赖和需要特定平台的活动。
比如:
- 连接到JMS(java消息服务)队列
- 处理一个WebService
- REST请求
- MQ队列的消息处理
- XMPP消息监听
- ......
总之,消息是和应用程序相关联的。
在您收到您的应用程序中的一个消息后,您必须决定该如何处理它。如果消息应该触发一个新的流程实例的开始,process instance的启动不应该使用runtimeService.startProcessInstanceByKey
,在以下方法中选择:
- ProcessInstance startProcessInstanceByMessage(String messageName);
- ProcessInstance startProcessInstanceByMessage(String messageName, Map<String, Object> processVariables);
- ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey, Map<String, Object> processVariables);
<definitions id="definitions"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="Examples"
xmlns:tns="Examples"> <message id="newInvoice" name="newInvoiceMessage" />
<message id="payment" name="paymentMessage" /> <process id="invoiceProcess"> <startEvent id="messageStart" >
<messageEventDefinition messageRef="newInvoice" />
</startEvent>
...
<intermediateCatchEvent id="paymentEvt" >
<messageEventDefinition messageRef="payment" />
</intermediateCatchEvent>
...
</process> </definitions>
有不同的方式来启动事件,Message Event Definitions 就非常有用了
例如订单可能来自call center ,也可以来自web shop
1.4 Start Events
开始事件总是捕捉型(Catching)的,比如一个消息接收,比如一个时间触发,总是有指定的触发。
<startEvent id="request" activiti:initiator="initiator" />
启动:
try {
identityService.setAuthenticatedUserId("bono");
runtimeService.startProcessInstanceByKey("request");
} finally {
identityService.setAuthenticatedUserId(null);
}
1.5 None Start Event
一个无启动事件在技术上意味着启动过程实例的触发器是未指定的。而且没有子元素节点。
一个有启动表单的例子:
<startEvent id="request" activiti:formKey="org/activiti/examples/taskforms/request.form" />
图形是一个空心圆:
1.6 Timer Start Event
时间启动事件,是一个时钟在中间的圆:
1.7 Boundary Events
边界事件是catching型的,连接到一个活动(一个边界事件永远不会throwing)的事件。
这意味着,当活动正在运行时,事件正在侦听某种类型的触发器。当事件被捕获时,该活动被中断,顺序流下行。
<boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport">
<timerEventDefinition>
<timeDuration>PT4H</timeDuration>
</timerEventDefinition>
</boundaryEvent>
<boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true">
<messageEventDefinition messageRef="newCustomerMessage"/>
</boundaryEvent>
2. Sequence Flow
2.1 Conditional sequence flow
带有UEL条件表达式的顺序流
<sequenceFlow id="flow" sourceRef="theStart" targetRef="theTask">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${order.price > 100 && order.price < 250}]]>
</conditionExpression>
</sequenceFlow>
2.2 Default sequence flow
任务和网关都可以有默认顺序流。
<exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" default="flow2" />
<sequenceFlow id="flow1" sourceRef="exclusiveGw" targetRef="task1">
<conditionExpression xsi:type="tFormalExpression">${conditionA}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="task2"/>
<sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="task3">
<conditionExpression xsi:type="tFormalExpression">${conditionB}</conditionExpression>
</sequenceFlow>
flow2就是默认顺序流,当所有条件不满足,则选择默认顺序流。
3 Gateways
3.1 Exclusive Gateway
异或网关
输入:只要有一个活动节点到达该网关那么就触发
输出:有多个输出点时,只会触发一个
<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>
3.2 Parallel Gateway
并行网关,可以有多个输入和输出(fork, join or both) ,实现AND逻辑
输入:该网关所有的输入节点都必须完成后才能触发该网关
输出:该网关的所有输出接点都将触发(除非转移条件不通过)
<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" />
3.3 Inclusive Gateway
Inclusive包容网关(Xor输入,And输出)
输入:只要有一个活动节点到达该网关那么就触发该网关(同XOR输入)
输出:该网关的所有输出接点都将触发(除非转移条件不通过)同AND输出
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="fork" /> <inclusiveGateway id="fork" />
<sequenceFlow sourceRef="fork" targetRef="receivePayment" >
<conditionExpression xsi:type="tFormalExpression">${paymentReceived == false}</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="fork" targetRef="shipOrder" >
<conditionExpression xsi:type="tFormalExpression">${shipOrder == true}</conditionExpression>
</sequenceFlow> <userTask id="receivePayment" name="Receive Payment" />
<sequenceFlow sourceRef="receivePayment" targetRef="join" /> <userTask id="shipOrder" name="Ship Order" />
<sequenceFlow sourceRef="shipOrder" targetRef="join" /> <inclusiveGateway id="join" />
<sequenceFlow sourceRef="join" targetRef="archiveOrder" /> <userTask id="archiveOrder" name="Archive Order" />
<sequenceFlow sourceRef="archiveOrder" targetRef="theEnd" /> <endEvent id="theEnd" />
4. TASKS
4.1 User Task
需要用户参与的任务
humanPerformer 方式,指定一个执行人
<userTask id='theTask' name='important task' >
<humanPerformer>
<resourceAssignmentExpression>
<formalExpression>kermit</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
potentialOwner 多个人或组
<userTask id='theTask' name='important task' >
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>user(kermit), group(management)</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
还有通过属性来设置
- <userTask id="theTask" name="my task" activiti:assignee="kermit" />
- <userTask id="theTask" name="my task" activiti:candidateUsers="kermit, gonzo" />
- <userTask id="theTask" name="my task" activiti:candidateGroups="management, accountancy" />
4.2 Script Task
略
4.3 Java Service Task
4.4 Business Rule Task
业务逻辑任务,使用JBoss Drools 规则引擎来处理输入输出;
<process id="simpleBusinessRuleProcess"> <startEvent id="theStart" />
<sequenceFlow sourceRef="theStart" targetRef="businessRuleTask" /> <businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}"
activiti:resultVariable="rulesOutput" /> <sequenceFlow sourceRef="businessRuleTask" targetRef="theEnd" /> <endEvent id="theEnd" /> </process>
4.5 Camel Task
camel.apache 规则引擎任务,一个例子
<process id="PingPongProcess">
<startEvent id="start"/>
<sequenceFlow id="flow1" sourceRef="start" targetRef="ping"/>
<serviceTask id="ping" activiti:type="camel"/>
<sequenceFlow id="flow2" sourceRef="ping" targetRef="saveOutput"/>
<serviceTask id="saveOutput" activiti:class="org.activiti.camel.examples.pingPong.SaveOutput" />
<sequenceFlow id="flow3" sourceRef="saveOutput" targetRef="end"/>
<endEvent id="end"/>
</process>
org.activiti.camel.examples.pingPong.SaveOutput类
@Override
public void configure() throws Exception {
from("activiti:PingPongProcess:ping").transform().simple("${property.input} World");
} 测试代码:
@Deployment
public void testPingPong() {
Map<String, Object> variables = new HashMap<String, Object>(); variables.put("input", "Hello");
Map<String, String> outputMap = new HashMap<String, String>();
variables.put("outputMap", outputMap); runtimeService.startProcessInstanceByKey("PingPongProcess", variables);
assertEquals(1, outputMap.size());
assertNotNull(outputMap.get("outputValue"));
assertEquals("Hello World", outputMap.get("outputValue"));
}
5. Sub-Processes and Call Activities 子流程
子流程是嵌入式的,不可重用,必须从None Start Event开始
而Call Activities本身调用的就是完整的流程
7. Process Initiation Authorization 初始化认证
<process id="potentialStarter">
<extensionElements>
<activiti:potentialStarter>
<resourceAssignmentExpression>
<formalExpression>group2, group(group3), user(user3)</formalExpression>
</resourceAssignmentExpression>
</activiti:potentialStarter>
</extensionElements> <startEvent id="theStart"/>
...
或
<process id="potentialStarter" activiti:candidateStarterUsers="user1, user2"
activiti:candidateStarterGroups="group1">
...
8. Data objects 流程数据
<process id="dataObjectScope" name="Data Object Scope" isExecutable="true">
<dataObject id="dObj123" name="StringTest123" itemSubjectRef="xsd:string">
<extensionElements>
<activiti:value>Testing123</activiti:value>
</extensionElements>
</dataObject>
...
9. Form properties 表单属性
- StartFormData FormService.getStartFormData(String processDefinitionId)
- TaskFormdata FormService.getTaskFormData(String taskId)
<userTask id="task">
<extensionElements>
<activiti:formProperty id="room" />
<activiti:formProperty id="duration" type="long"/>
<activiti:formProperty id="speaker" variable="SpeakerName" writable="false" />
<activiti:formProperty id="street" expression="#{address.street}" required="true" />
</extensionElements>
</userTask>
表单属性 room 对应--〉 流程变量 room 类型: String
表单属性 duration 对应--〉 流程变量 duration 类型: java.lang.Long
表单属性 speaker 对应--〉 流程变量 SpeakerName. 它是TaskFormData对象.
表单属性 street 对应--〉 Java bean 属性 street定义在流程变量 address
<startEvent id="start">
<extensionElements>
<activiti:formProperty id="speaker"
name="Speaker"
variable="SpeakerName"
type="string" /> <activiti:formProperty id="start"
type="date"
datePattern="dd-MMM-yyyy" /> <activiti:formProperty id="direction" type="enum">
<activiti:value id="left" name="Go Left" />
<activiti:value id="right" name="Go Right" />
<activiti:value id="up" name="Go Up" />
<activiti:value id="down" name="Go Down" />
</activiti:formProperty> </extensionElements>
</startEvent>
<startEvent>
<extensionElements>
<activiti:formProperty id="numberOfDays" name="Number of days" value="${numberOfDays}" type="long" required="true"/>
<activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" value="${startDate}" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
<activiti:formProperty id="vacationMotivation" name="Motivation" value="${vacationMotivation}" type="string" />
</extensionElements>
</userTask>
表单:
10. External form rendering 外部表单集成
提交表单属性,第三方表单系统发送数据:
ProcessInstance FormService.submitStartFormData(String processDefinitionId, Map<String,String> properties) FormService.submitTaskFormData(String taskId, Map<String,String> properties)
获得属性,Acticiti接收处理数据:
StartFormData FormService.getStartFormData(String processDefinitionId)
TaskFormdata FormService.getTaskFormData(String taskId)
.
Liferay7 BPM门户开发之2: BPMN 2.0 规范入门 (Activiti BPMN extensions)的更多相关文章
- Liferay7 BPM门户开发之10: 通用流程实现从Servlet到Portlet(Part1)
开发目的: 实现通用流程自动化处理(即实现不需要hardcode代码的bpm统一处理后台,仅需要写少量前端html form代码和拖拽设计BPM定义) 既可独立运行或可依托于Liferay或依托其它门 ...
- Liferay7 BPM门户开发之37: Liferay7下的OSGi Hook集成开发
hook开发是Liferay客制扩展的一种方式,比插件灵活,即可以扩展liferay门户,也能对原有特性进行更改,Liferay有许多内置的服务,比如用hook甚至可以覆盖Liferay服务. 可作为 ...
- Liferay7 BPM门户开发之17: Portlet 生命周期
Portlet 生命周期 init() =〉 render() =〉 processAction() =〉 processEvent() =〉 serveResource() =〉destroy() ...
- Liferay7 BPM门户开发之44: 集成Activiti展示流程列表
处理依赖关系 集成Activiti之前,必须搞清楚其中的依赖关系,才能在Gradle里进行配置. 依赖关系: 例如,其中activiti-engine依赖于activiti-bpmn-converte ...
- Liferay7 BPM门户开发之12:acitiviti和liferay用户权限体系集成
写到第12章才出现Liferay的内容,希望可以厚积薄发. 我们的目标是不使用不维护Activiti的用户组织架构,只维护Liferay的体系,这样的好处是非常明显的,即不用做组织架构的同步工作. 原 ...
- Liferay7 BPM门户开发之5: Activiti和Spring集成
参考文档: https://github.com/jbarrez/spring-boot-with-activiti-examplehttps://github.com/sxyx2008/spring ...
- Liferay7 BPM门户开发之3: Activiti开发环境搭建
下载地址: http://activiti.org/download.html 源码: https://github.com/Activiti/Activiti 环境准备(检查项): JDK 1.7 ...
- Liferay7 BPM门户开发之45: 集成Activiti文件上传部署流程BPMN模型
开发文件上传,部署流程模板. 首先,开发jsp页面,deploy.jsp <%@ include file="/init.jsp" %> <h3>${RET ...
- Liferay7 BPM门户开发之32: 实现自定义认证登陆(定制Authentication Hook)
第一步:修改liferay-hook.xml <?xml version="1.0"?> <!DOCTYPE hook PUBLIC "-//Lifer ...
随机推荐
- oracle count 大表
刚从生产环境导了一个大表到测试环境,迫不及待的要好好玩弄一下. 1.coun(1) ) from table_name; 条数: 567979280 时间:4:47 2.count 索引字段 sele ...
- mysql 外键理解
假定一个班级的学生个人信息表: 什么是外键 在设计的时候,就给表1加入一个外键,这个外键就是表2中的学号字段,那么这样表1就是主表,表2就是子表.(注意: 外键不一定须要作为从表的主键.外键也不一定是 ...
- mongodb知识积累
1: 安装mongodb https://www.cnblogs.com/zhangdaicong/p/7492494.html 2:配置文件 vi /etc/mongodb.conf https:/ ...
- SQL中的split方法的使用
参数说明: 1.@String :需要split的字符串 2.@Delimiter :格式化时分隔符 3.@index :返回split后数组的值 ), ),)) ) AS BEGIN )) ) DE ...
- c语言spline
#define NRANSI #include "nrutil.h" void spline(float x[], float y[], int n, float yp1, flo ...
- HDU 1517 A Multiplication Game (SG函数找规律)
题意:两个玩家玩一个游戏,从 p = 1,开始,然后依次轮流选择一个2 - 9的数乘以 p,问你谁先凑够 p >= n. 析:找规律,我先打了一下SG函数的表,然后就找到规律了 我找到的是: 1 ...
- ERR_CACHE_MISS 上一页提示确认重新提交表单
SITUATION: 设备搜索后,根据返回结果 list.php 进入特定设备的详细页面 one.php,但点击后退按钮(上一页)返回 list.php,会出现确认重新提交表单的错误页面 ERR_CA ...
- usb_ctrl
IFCLK应该是clkout,与数据同步 flagd就是就是slcs FPGA的系统时钟用FX2LP的clkout时钟,个人觉得关键点在时钟是48M,别的异步时钟也是可以的. 模式配置 使用的模式是从 ...
- asp.net DataReader DataTable 使用反射给给实体赋值
asp.net 使用反射给给实体赋值 实体类继承此基类 using System.Reflection; using System.Data.SqlClient; using System.Data; ...
- Linux磁盘满问题分析
线上一台Linux服务器最近经常磁盘根分区满告警,但不是普通的日志文件或数据文件过多过大,现象如下: 1)执行“df -h”查看各分区空间的使用情况[root@XEN64 /]# df -hFiles ...