您的位置:首页 > Web前端

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外部表单集成

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%20event

1.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

业务逻辑任务,使用JBossDrools规则引擎来处理输入输出;

<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)
.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: