Liferay7 BPM门户开发之2: BPMN 2.0 规范入门 (Activiti BPMN extensions)
2016-10-09 16:35
405 查看
Liferay最大的问题是BPM弱,如果做企业开发,BPM必不可少,所以直入主题,做个BPMN2入门.
本文参考地址:http://activiti.org/userguide/index.html#bpmnConstructs
BPMN2.0中的重要概念:
Events事件
SequenceFlow顺序流
Gateways网关
Tasks任务
Sub-ProcessesandCallActivities子流程
TransactionsandConcurrency事务并发
ProcessInitiationAuthorization初始化认证
Dataobjects流程数据
其他相关项:
Formproperties表单属性
Externalformrendering外部表单集成
startevent
intermediateevent
boundaryevent
必须有确切的一个元素,分别是:
timeDate
<timerEventDefinition><timeDate>2011-03-11T12:13:14</timeDate></timerEventDefinition>
在确切的时间点执行
timeDuration
<timerEventDefinition><timeDuration>P10D</timeDuration></timerEventDefinition>
从最后一个任务完成后10天开始执行
timeCycle
<timerEventDefinition>
<timeCycleactiviti:endDate="2015-02-25T16:42:11+00:00">R3/PT10H</timeCycle>
</timerEventDefinition>
或者变量形式:
<timerEventDefinition>
<timeCycle>R3/PT10H/${EndDate}</timeCycle>
</timerEventDefinition>
循环3次,间隔10小时
也可以使用cronexpressions:http://www.quartz-scheduler.org/documentation/
比如:
连接到JMS(java消息服务)队列
处理一个WebService
REST请求
MQ队列的消息处理
XMPP消息监听
......
总之,消息是和应用程序相关联的。
在您收到您的应用程序中的一个消息后,您必须决定该如何处理它。如果消息应该触发一个新的流程实例的开始,processinstance的启动不应该使用
ProcessInstancestartProcessInstanceByMessage(StringmessageName);
ProcessInstancestartProcessInstanceByMessage(StringmessageName,Map<String,Object>processVariables);
ProcessInstancestartProcessInstanceByMessage(StringmessageName,StringbusinessKey,Map<String,Object>processVariables);
有不同的方式来启动事件,MessageEventDefinitions就非常有用了
例如订单可能来自callcenter,也可以来自webshop
<startEventid="request"activiti:initiator="initiator"/>
启动:
一个有启动表单的例子:
<startEventid="request"activiti:formKey="org/activiti/examples/taskforms/request.form"/>
图形是一个空心圆:
1.7BoundaryEvents
边界事件是catching型的,连接到一个活动(一个边界事件永远不会throwing)的事件。
这意味着,当活动正在运行时,事件正在侦听某种类型的触发器。当事件被捕获时,该活动被中断,顺序流下行。
带有UEL条件表达式的顺序流
2.2Defaultsequenceflow
任务和网关都可以有默认顺序流。
flow2就是默认顺序流,当所有条件不满足,则选择默认顺序流。
异或网关
输入:只要有一个活动节点到达该网关那么就触发
输出:有多个输出点时,只会触发一个
3.2ParallelGateway
并行网关,可以有多个输入和输出(fork,joinorboth),实现AND逻辑
输入:该网关所有的输入节点都必须完成后才能触发该网关
输出:该网关的所有输出接点都将触发(除非转移条件不通过)
输入:只要有一个活动节点到达该网关那么就触发该网关(同XOR输入)
输出:该网关的所有输出接点都将触发(除非转移条件不通过)同AND输出
需要用户参与的任务
humanPerformer方式,指定一个执行人
还有通过属性来设置
<userTaskid="theTask"name="mytask"activiti:assignee="kermit"/>
<userTaskid="theTask"name="mytask"activiti:candidateUsers="kermit,gonzo"/>
<userTaskid="theTask"name="mytask"activiti:candidateGroups="management,accountancy"/>
4.2ScriptTask
略
4.3JavaServiceTask
4.4BusinessRuleTask
业务逻辑任务,使用JBossDrools规则引擎来处理输入输出;
4.5CamelTask
camel.apache规则引擎任务,一个例子
而CallActivities本身调用的就是完整的流程
StartFormDataFormService.getStartFormData(StringprocessDefinitionId)
TaskFormdataFormService.getTaskFormData(StringtaskId)
表单属性room对应--〉流程变量room类型:String
表单属性duration对应--〉流程变量duration类型:java.lang.Long
表单属性speaker对应--〉流程变量SpeakerName.它是TaskFormData对象.
表单属性street对应--〉Javabean属性street定义在流程变量address
表单:
提交表单属性,第三方表单系统发送数据:
获得属性,Acticiti接收处理数据:
本文参考地址:http://activiti.org/userguide/index.html#bpmnConstructs
BPMN2.0中的重要概念:
Events事件
SequenceFlow顺序流
Gateways网关
Tasks任务
Sub-ProcessesandCallActivities子流程
TransactionsandConcurrency事务并发
ProcessInitiationAuthorization初始化认证
Dataobjects流程数据
其他相关项:
Formproperties表单属性
Externalformrendering外部表单集成
1、Events
1、1TimerEventDefinitions
由时间触发的时间,用于startevent
intermediateevent
boundaryevent
必须有确切的一个元素,分别是:
timeDate
<timerEventDefinition><timeDate>2011-03-11T12:13:14</timeDate></timerEventDefinition>
在确切的时间点执行
timeDuration
<timerEventDefinition><timeDuration>P10D</timeDuration></timerEventDefinition>
从最后一个任务完成后10天开始执行
timeCycle
<timerEventDefinition>
<timeCycleactiviti:endDate="2015-02-25T16:42:11+00:00">R3/PT10H</timeCycle>
</timerEventDefinition>
或者变量形式:
<timerEventDefinition>
<timeCycle>R3/PT10H/${EndDate}</timeCycle>
</timerEventDefinition>
循环3次,间隔10小时
也可以使用cronexpressions:http://www.quartz-scheduler.org/documentation/
1.2SignalEventDefinitions
一个例子:https://github.com/chanjarster/activiti-learn/wiki/%E8%AF%A6%E8%A7%A3signal%20event1.3MessageEventDefinitions
想象一下,作为一个嵌入式的流程引擎(不是国内很多固化Hardcode式的流程引擎),Activiti关心的是实际从第三方应用系统接收的消息。这将是环境依赖和需要特定平台的活动。比如:
连接到JMS(java消息服务)队列
处理一个WebService
REST请求
MQ队列的消息处理
XMPP消息监听
......
总之,消息是和应用程序相关联的。
在您收到您的应用程序中的一个消息后,您必须决定该如何处理它。如果消息应该触发一个新的流程实例的开始,processinstance的启动不应该使用
runtimeService.startProcessInstanceByKey,在以下方法中选择:
ProcessInstancestartProcessInstanceByMessage(StringmessageName);
ProcessInstancestartProcessInstanceByMessage(StringmessageName,Map<String,Object>processVariables);
ProcessInstancestartProcessInstanceByMessage(StringmessageName,StringbusinessKey,Map<String,Object>processVariables);
<definitionsid="definitions" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="Examples" xmlns:tns="Examples"> <messageid="newInvoice"name="newInvoiceMessage"/> <messageid="payment"name="paymentMessage"/> <processid="invoiceProcess"> <startEventid="messageStart"> <messageEventDefinitionmessageRef="newInvoice"/> </startEvent> ... <intermediateCatchEventid="paymentEvt"> <messageEventDefinitionmessageRef="payment"/> </intermediateCatchEvent> ... </process> </definitions>
有不同的方式来启动事件,MessageEventDefinitions就非常有用了
例如订单可能来自callcenter,也可以来自webshop
1.4StartEvents
开始事件总是捕捉型(Catching)的,比如一个消息接收,比如一个时间触发,总是有指定的触发。<startEventid="request"activiti:initiator="initiator"/>
启动:
try{ identityService.setAuthenticatedUserId("bono"); runtimeService.startProcessInstanceByKey("request"); }finally{ identityService.setAuthenticatedUserId(null); }
1.5NoneStartEvent
一个无启动事件在技术上意味着启动过程实例的触发器是未指定的。而且没有子元素节点。一个有启动表单的例子:
<startEventid="request"activiti:formKey="org/activiti/examples/taskforms/request.form"/>
图形是一个空心圆:
1.6TimerStartEvent
时间启动事件,是一个时钟在中间的圆:1.7BoundaryEvents
边界事件是catching型的,连接到一个活动(一个边界事件永远不会throwing)的事件。
这意味着,当活动正在运行时,事件正在侦听某种类型的触发器。当事件被捕获时,该活动被中断,顺序流下行。
<boundaryEventid="escalationTimer"cancelActivity="true"attachedToRef="firstLineSupport"> <timerEventDefinition> <timeDuration>PT4H</timeDuration> </timerEventDefinition> </boundaryEvent>
<boundaryEventid="boundary"attachedToRef="task"cancelActivity="true"> <messageEventDefinitionmessageRef="newCustomerMessage"/> </boundaryEvent>
2.SequenceFlow
2.1Conditionalsequenceflow带有UEL条件表达式的顺序流
<sequenceFlowid="flow"sourceRef="theStart"targetRef="theTask"> <conditionExpressionxsi:type="tFormalExpression"> <![CDATA[${order.price>100&&order.price<250}]]> </conditionExpression> </sequenceFlow>
2.2Defaultsequenceflow
任务和网关都可以有默认顺序流。
<exclusiveGatewayid="exclusiveGw"name="ExclusiveGateway"default="flow2"/> <sequenceFlowid="flow1"sourceRef="exclusiveGw"targetRef="task1"> <conditionExpressionxsi:type="tFormalExpression">${conditionA}</conditionExpression> </sequenceFlow> <sequenceFlowid="flow2"sourceRef="exclusiveGw"targetRef="task2"/> <sequenceFlowid="flow3"sourceRef="exclusiveGw"targetRef="task3"> <conditionExpressionxsi:type="tFormalExpression">${conditionB}</conditionExpression> </sequenceFlow>
flow2就是默认顺序流,当所有条件不满足,则选择默认顺序流。
3Gateways
3.1ExclusiveGateway异或网关
输入:只要有一个活动节点到达该网关那么就触发
输出:有多个输出点时,只会触发一个
<exclusiveGatewayid="exclusiveGw"name="ExclusiveGateway"/> <sequenceFlowid="flow2"sourceRef="exclusiveGw"targetRef="theTask1"> <conditionExpressionxsi:type="tFormalExpression">${input==1}</conditionExpression> </sequenceFlow> <sequenceFlowid="flow3"sourceRef="exclusiveGw"targetRef="theTask2"> <conditionExpressionxsi:type="tFormalExpression">${input==2}</conditionExpression> </sequenceFlow> <sequenceFlowid="flow4"sourceRef="exclusiveGw"targetRef="theTask3"> <conditionExpressionxsi:type="tFormalExpression">${input==3}</conditionExpression> </sequenceFlow>
3.2ParallelGateway
并行网关,可以有多个输入和输出(fork,joinorboth),实现AND逻辑
输入:该网关所有的输入节点都必须完成后才能触发该网关
输出:该网关的所有输出接点都将触发(除非转移条件不通过)
<startEventid="theStart"/> <sequenceFlowid="flow1"sourceRef="theStart"targetRef="fork"/> <parallelGatewayid="fork"/> <sequenceFlowsourceRef="fork"targetRef="receivePayment"/> <sequenceFlowsourceRef="fork"targetRef="shipOrder"/> <userTaskid="receivePayment"name="ReceivePayment"/> <sequenceFlowsourceRef="receivePayment"targetRef="join"/> <userTaskid="shipOrder"name="ShipOrder"/> <sequenceFlowsourceRef="shipOrder"targetRef="join"/> <parallelGatewayid="join"/> <sequenceFlowsourceRef="join"targetRef="archiveOrder"/> <userTaskid="archiveOrder"name="ArchiveOrder"/> <sequenceFlowsourceRef="archiveOrder"targetRef="theEnd"/> <endEventid="theEnd"/>
3.3InclusiveGateway
Inclusive包容网关(Xor输入,And输出)输入:只要有一个活动节点到达该网关那么就触发该网关(同XOR输入)
输出:该网关的所有输出接点都将触发(除非转移条件不通过)同AND输出
<startEventid="theStart"/> <sequenceFlowid="flow1"sourceRef="theStart"targetRef="fork"/> <inclusiveGatewayid="fork"/> <sequenceFlowsourceRef="fork"targetRef="receivePayment"> <conditionExpressionxsi:type="tFormalExpression">${paymentReceived==false}</conditionExpression> </sequenceFlow> <sequenceFlowsourceRef="fork"targetRef="shipOrder"> <conditionExpressionxsi:type="tFormalExpression">${shipOrder==true}</conditionExpression> </sequenceFlow> <userTaskid="receivePayment"name="ReceivePayment"/> <sequenceFlowsourceRef="receivePayment"targetRef="join"/> <userTaskid="shipOrder"name="ShipOrder"/> <sequenceFlowsourceRef="shipOrder"targetRef="join"/> <inclusiveGatewayid="join"/> <sequenceFlowsourceRef="join"targetRef="archiveOrder"/> <userTaskid="archiveOrder"name="ArchiveOrder"/> <sequenceFlowsourceRef="archiveOrder"targetRef="theEnd"/> <endEventid="theEnd"/>
4.TASKS
4.1UserTask需要用户参与的任务
humanPerformer方式,指定一个执行人
<userTaskid='theTask'name='importanttask'> <humanPerformer> <resourceAssignmentExpression> <formalExpression>kermit</formalExpression> </resourceAssignmentExpression> </humanPerformer> </userTask>
potentialOwner多个人或组
<userTaskid='theTask'name='importanttask'> <potentialOwner> <resourceAssignmentExpression> <formalExpression>user(kermit),group(management)</formalExpression> </resourceAssignmentExpression> </potentialOwner> </userTask>
还有通过属性来设置
<userTaskid="theTask"name="mytask"activiti:assignee="kermit"/>
<userTaskid="theTask"name="mytask"activiti:candidateUsers="kermit,gonzo"/>
<userTaskid="theTask"name="mytask"activiti:candidateGroups="management,accountancy"/>
4.2ScriptTask
略
4.3JavaServiceTask
4.4BusinessRuleTask
业务逻辑任务,使用
<processid="simpleBusinessRuleProcess"> <startEventid="theStart"/> <sequenceFlowsourceRef="theStart"targetRef="businessRuleTask"/> <businessRuleTaskid="businessRuleTask"activiti:ruleVariablesInput="${order}" activiti:resultVariable="rulesOutput"/> <sequenceFlowsourceRef="businessRuleTask"targetRef="theEnd"/> <endEventid="theEnd"/> </process>
4.5CamelTask
camel.apache规则引擎任务,一个例子
<processid="PingPongProcess"> <startEventid="start"/> <sequenceFlowid="flow1"sourceRef="start"targetRef="ping"/> <serviceTaskid="ping"activiti:type="camel"/> <sequenceFlowid="flow2"sourceRef="ping"targetRef="saveOutput"/> <serviceTaskid="saveOutput"activiti:class="org.activiti.camel.examples.pingPong.SaveOutput"/> <sequenceFlowid="flow3"sourceRef="saveOutput"targetRef="end"/> <endEventid="end"/> </process>
org.activiti.camel.examples.pingPong.SaveOutput类
@Override publicvoidconfigure()throwsException{ from("activiti:PingPongProcess:ping").transform().simple("${property.input}World"); } 测试代码:
@Deployment publicvoidtestPingPong(){ Map<String,Object>variables=newHashMap<String,Object>(); variables.put("input","Hello"); Map<String,String>outputMap=newHashMap<String,String>(); variables.put("outputMap",outputMap); runtimeService.startProcessInstanceByKey("PingPongProcess",variables); assertEquals(1,outputMap.size()); assertNotNull(outputMap.get("outputValue")); assertEquals("HelloWorld",outputMap.get("outputValue")); }
5.Sub-ProcessesandCallActivities子流程
子流程是嵌入式的,不可重用,必须从NoneStartEvent开始而CallActivities本身调用的就是完整的流程
7.ProcessInitiationAuthorization初始化认证
<processid="potentialStarter">
<extensionElements>
<activiti:potentialStarter>
<resourceAssignmentExpression>
<formalExpression>group2,group(group3),user(user3)</formalExpression>
</resourceAssignmentExpression>
</activiti:potentialStarter>
</extensionElements>
<startEventid="theStart"/>
...
或
<processid="potentialStarter"activiti:candidateStarterUsers="user1,user2"
activiti:candidateStarterGroups="group1">
...
8.Dataobjects流程数据
<processid="dataObjectScope"name="DataObjectScope"isExecutable="true">
<dataObjectid="dObj123"name="StringTest123"itemSubjectRef="xsd:string">
<extensionElements>
<activiti:value>Testing123</activiti:value>
</extensionElements>
</dataObject>
...
9.Formproperties表单属性
StartFormDataFormService.getStartFormData(StringprocessDefinitionId)TaskFormdataFormService.getTaskFormData(StringtaskId)
<userTaskid="task">
<extensionElements>
<activiti:formPropertyid="room"/>
<activiti:formPropertyid="duration"type="long"/>
<activiti:formPropertyid="speaker"variable="SpeakerName"writable="false"/>
<activiti:formPropertyid="street"expression="#{address.street}"required="true"/>
</extensionElements>
</userTask>
表单属性room对应--〉流程变量room类型:String
表单属性duration对应--〉流程变量duration类型:java.lang.Long
表单属性speaker对应--〉流程变量SpeakerName.它是TaskFormData对象.
表单属性street对应--〉Javabean属性street定义在流程变量address
<startEventid="start">
<extensionElements>
<activiti:formPropertyid="speaker"
name="Speaker"
variable="SpeakerName"
type="string"/>
<activiti:formPropertyid="start"
type="date"
datePattern="dd-MMM-yyyy"/>
<activiti:formPropertyid="direction"type="enum">
<activiti:valueid="left"name="GoLeft"/>
<activiti:valueid="right"name="GoRight"/>
<activiti:valueid="up"name="GoUp"/>
<activiti:valueid="down"name="GoDown"/>
</activiti:formProperty>
</extensionElements>
</startEvent>
<startEvent>
<extensionElements>
<activiti:formPropertyid="numberOfDays"name="Numberofdays"value="${numberOfDays}"type="long"required="true"/>
<activiti:formPropertyid="startDate"name="Firstdayofholiday(dd-MM-yyy)"value="${startDate}"datePattern="dd-MM-yyyyhh:mm"type="date"required="true"/>
<activiti:formPropertyid="vacationMotivation"name="Motivation"value="${vacationMotivation}"type="string"/>
</extensionElements>
</userTask>
表单:
10.Externalformrendering外部表单集成
提交表单属性,第三方表单系统发送数据:ProcessInstanceFormService.submitStartFormData(StringprocessDefinitionId,Map<String,String>properties)
FormService.submitTaskFormData(StringtaskId,Map<String,String>properties)
获得属性,Acticiti接收处理数据:
StartFormDataFormService.getStartFormData(StringprocessDefinitionId)
TaskFormdataFormService.getTaskFormData(StringtaskId).
相关文章推荐
- Liferay7 BPM门户开发之6: Activiti数据库换为mysql
- Liferay7 BPM门户开发之5: Activiti和Spring集成
- Liferay7 BPM门户开发之11: Activiti工作流程开发的一些统一规则和实现原理(完整版)
- Liferay7 BPM门户开发之8: Activiti实用问题集合
- Liferay7 BPM门户开发之44: 集成Activiti展示流程列表
- Liferay7 BPM门户开发之45: 集成Activiti文件上传部署流程BPMN模型
- Liferay7 BPM门户开发之26: 集成Activiti到Liferay7
- Liferay7 BPM门户开发之3: Activiti开发环境搭建
- Liferay7 BPM门户开发之46: 集成Activiti用户、用户组、成员关系同步
- Liferay7 BPM门户开发之38: OSGi模块化Bndtools、Maven、Gradle开发构建入门
- Liferay7 BPM门户开发之15: Liferay开发体系简介
- Liferay7 BPM门户开发之18: 理解ServiceContext
- Liferay7 BPM门户开发之35: AssetTag的集成查询
- Liferay7 BPM门户开发之32: 实现自定义认证登陆(定制Authentication Hook)
- Liferay7 BPM门户开发之36: 使用Portlet filters过滤器做切面AOP
- Liferay7 BPM门户开发之27: MVC Portlet插件工程开发
- Activiti学习——BPMN2.0规范说明
- Liferay7 BPM门户开发之28: Portlet文件上传,及实体类同步更新上传
- BPMN2.0规范及iactiviti
- Liferay7 BPM门户开发之12:acitiviti和liferay用户权限体系集成