• 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).
    关于作者: 王昕(QQ:475660) 在广州工作生活30余年。十多年开发经验,在Java、即时通讯、NoSQL、BPM、大数据等领域较有经验。
    目前维护的开源产品:https://gitee.com/475660
  • 相关阅读:
    事务使用测试结论
    Enum枚举2
    Enum枚举
    sqlserver数据库存取图片
    sqlserver数据库操作公共类DBOperate
    SqlParamsTool
    sqlserver数据库 -- SqlParameter使用
    sqlserver 数据库连接状态判断
    水晶报表2
    防止系统页面被加载进 iframe 子窗口
  • 原文地址:https://www.cnblogs.com/sea520/p/13646918.html
Copyright © 2020-2023  润新知