activiti實戰讀書筆記——第十一章 事件

1、啓動事件 java

每一個流程都須要從啓動事件開始,根據不一樣的需求有空啓動、定時啓動、異常啓動和消息啓動。 ide

異常啓動事件不能用於主流程,必須嵌入到事件子流程中。 函數

一、定時啓動事件 工具

定時啓動標籤timerEventDefinition嵌套在啓動事件startEvent中就構成了定時啓動事件。定時啓動事件能夠有三種啓動屬性: 測試

以下定義了一個定時啓動事件,部署流程5分鐘後啓動: this


<startEvent id="timerstartevent1" name="Timer start">
      <timerEventDefinition>
        <timeDuration>PT5M</timeDuration>
      </timerEventDefinition>
    </startEvent>

定時啓動的測試代碼以下: spa

@Deployment(resources = "chapter11/timerEvent/timerStartEvent.bpmn")
    public void testTriggerAutomatic() throws Exception {
        // 部署以後引擎會自動建立一個定時啓動事件的Job
        JobQuery jobQuery = managementService.createJobQuery();
        assertEquals(1, jobQuery.count());

        // 模擬時間5分鐘以後
        SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
        System.out.println(".... start ...." + sdf.format(new Date()));
        Context.getProcessEngineConfiguration().getClock().setCurrentTime(new Date(System.currentTimeMillis() + ((50 * 60 * 1000) + 5000)));
        waitForJobExecutorToProcessAllJobs(5000L, 1L);
        System.out.println(".... end ...." + sdf.format(new Date()));

        assertEquals(0, jobQuery.count());

        // 檢查是否啓動了流程實例
        long count = runtimeService.createProcessInstanceQuery().processDefinitionKey("timerStartEvent").count();
        assertEquals(1, count);
    }



Context.getProcessEngineConfiguration().getClock().setCurrentTime(new Date(System.currentTimeMillis() + ((50 * 60 * 1000) + 5000)));
        waitForJobExecutorToProcessAllJobs(5000L, 1L);
這兩行代碼用activiti提供的測試工具用5s模擬5分鐘,我本身運行的時候發現Context.getProcessEngineConfiguration()的結果是null,改用測試類繼承的PluggableActivitiTestCase類的getProcessEngineConfiguration()後正常執行。


猜測是做者搞混了?Context.getProcessEngineConfiguration()是非測試時用的代碼? code

定時啓動事件也能夠手動觸發,即API調用觸發,測試代碼以下: orm

@Deployment(resources = "chapter11/timerEvent/timerStartEvent.bpmn")
    public void testTriggerManual() throws Exception {
        // 部署以後引擎會自動建立一個定時啓動事件的Job
        JobQuery jobQuery = managementService.createJobQuery();
        assertEquals(1, jobQuery.count());

        // 手動觸發做業的執行
        Job job = jobQuery.singleResult();
        managementService.executeJob(job.getId());

        assertEquals(0, jobQuery.count());

        // 檢查是否啓動了流程實例
        long count = runtimeService.createProcessInstanceQuery().processDefinitionKey("timerStartEvent").count();
        assertEquals(1, count);
    }

定時啓動的job唄記錄在ACT_RU_JOB表中,如下是該表的一些主要字段: xml

二、消息啓動事件

經過一個消息標誌觸發啓動事件。

消息啓動事件的相關代碼:

<message id="MSG_001" name="啓動XXX流程"></message>
  <process id="messageStartEvent" name="messageStartEvent" isExecutable="true">
    <startEvent id="messagestartevent1" name="Start">
      <messageEventDefinition messageRef="MSG_001"></messageEventDefinition>
    </startEvent>
    <userTask id="usertask1" name="用戶任務" activiti:assignee="kermit"></userTask>
    <sequenceFlow id="flow1" sourceRef="messagestartevent1" targetRef="usertask1"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
  </process>



其中message標籤訂義了一個消息,id屬性在流程定義文件內部引用,name屬性是代碼中啓動事件的啓動參數。

以消息啓動事件的測試代碼:

public void testStartMessageEvent(){
		ProcessInstance processInstance = this.runtimeService.startProcessInstanceByMessage("啓動流程");
		Assert.assertNotNull(processInstance);
	}



也可使用startProcessInstanceByKey方法來啓動流程,可是這對於消息啓動事件有一個限制:流程只能包含一個消息啓動事件。測試代碼以下:

public void testStartMessageEventByKey() throws Exception {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("messageStartEvent");
        assertNotNull(processInstance);
    }



對於消息,在部署流程以後引擎會在初始化中處理消息事件,把消息的類型註冊到ACT_RU_EVENT_SUBSCR表中,在流程執行過程當中遇到了消息類型時間或經過API觸發消息事件會從該表中讀取數據,而且根據消息的屬性調用消息處理器。表的主要字段以下:
  • EVENT_TYPE_:時間類型。message表示消息類型;
  • EVENT_NAME_:消息名稱。觸發消息時引擎會根據消息名稱參數和這個字段進行匹配;
  • EXECUTION_ID_:實例執行ID。若是一個流程以消息啓動事件引導,則此字段爲空,若是在流程運行中註冊了消息事件,則此字段保存流程執行ID;
  • PROC_INST_ID_:流程實例ID。通EXECUTION_ID_,保存流程實例ID(與EXECUTION是一對多的關係);
  • ACTIVITI_ID_:活動ID 。指示若是根據字段EVENT_NAME_匹配到消息就應該觸發哪一個活動;
  • CONFIGURATION_:消息的匹配信息。對於消息類型來講這裏保存的是流程定義ID。

測試消息註冊成功的代碼:

@Test
	@Deployment(resources = "messageStartEvent.bpmn")
	public void testMessageSubcription(){
		EventSubscriptionQueryImpl query = new EventSubscriptionQueryImpl(this.processEngineConfiguration.getCommandExecutor());
		EventSubscriptionEntity entity = query.eventName("啓動流程").singleResult();
		Assert.assertNotNull(entity);
	}



沒寫完,要出門了,晚上回來繼續。BTW,今天北京天氣不錯,這會出門還能曬到太陽,下午安。

回來了,筆記繼續。

2、終止結束事件

終止結束事件和空結束事件的區別在於終止結束事件能夠終止整個流程實例的執行,而空結束事件只結束一條輸出流的執行。

上圖的流程中啓動事件後經過一個並行網關獲得一個主任務流程和一個子任務流程。若是主任務完成後到達終止結束事件則整個流程都將結束(包括進行中的子任務),但若是子任務先於主任務結束,則主任務還未結束,整個流程還處於運行狀態。

終止結束事件的XML定義以下:


<endEvent id="terminateendevent1" name="End">
      <terminateEventDefinition></terminateEventDefinition>
    </endEvent>



在測試代碼中使用了以下函數:


private void checkFinished(ProcessInstance processInstance) {
        // 驗證流程已結束
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstance.getProcessInstanceId()).singleResult();
        assertNotNull(historicProcessInstance.getEndTime());

        // 查詢歷史任務
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().list();
        for (HistoricTaskInstance hti : list) {
            System.out.println(hti.getName() + "  " + hti.getDeleteReason());
        }

        // 流程結束後校驗監聽器設置的變量
        HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery().variableName("settedOnEnd").singleResult();
        assertEquals(true, variableInstance.getValue());
    }

其中HistoricTaskInstance.getDeleteReason()獲得任務的刪除緣由屬性,若是任務正常完成則屬性爲complete,若是異常終止爲delete。


3、邊界事件

一、異常邊界事件

下面的xml定義中在一個java service添加了異常邊界事件


<process id="throwErrorManual" name="throwErrorManual" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <serviceTask id="servicetask1" name="自動執行系統任務" activiti:class="me.kafeitu.activiti.chapter11.listener.ThrowErrorManaualService"></serviceTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow2" sourceRef="servicetask1" targetRef="endevent1"></sequenceFlow>
    <boundaryEvent id="boundaryerror1" name="Error" attachedToRef="servicetask1">
      <errorEventDefinition errorRef="AIA_ERROR_99"></errorEventDefinition>
    </boundaryEvent>
    <userTask id="usertask1" name="處理異常"></userTask>
    <sequenceFlow id="flow3" sourceRef="boundaryerror1" targetRef="usertask1"></sequenceFlow>
    <sequenceFlow id="flow4" sourceRef="usertask1" targetRef="servicetask1"></sequenceFlow>
    <textAnnotation id="textannotation1">
      <text>異常代碼:AIA_ERROR_99</text>
    </textAnnotation>
    <association id="association1" sourceRef="textannotation1" targetRef="boundaryerror1"></association>
  </process>



其中java service的實現類代碼以下:
public class ThrowErrorManaualService implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) throws Exception {
        if (execution.getVariable("pass") == null) {
            throw new BpmnError("AIA_ERROR_99");
        }
    }
}

二、消息邊界事件

定義消息邊界事件的代碼以下:


<process id="messageBoundaryEvent" name="messageBoundaryEvent" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="審覈文件"></userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
    <boundaryEvent id="boundarymessage1" name="Message" attachedToRef="usertask1" cancelActivity="true">
      <messageEventDefinition messageRef="MSG_HELP"></messageEventDefinition>
    </boundaryEvent>
    <userTask id="usertask2" name="協助處理"></userTask>
    <sequenceFlow id="flow3" sourceRef="boundarymessage1" targetRef="usertask2"></sequenceFlow>
    <sequenceFlow id="flow4" sourceRef="usertask2" targetRef="usertask1"></sequenceFlow>
  </process>



其中<boundaryEvent>中有一個cancelActiviti屬性,在消息邊界事件中屬性爲true表示消息邊界事件觸發後已註冊的消息被刪除;爲false表示不刪除,消息能夠被重複觸發。測試代碼中:


ExecutionQuery executionQuery = runtimeService.createExecutionQuery().messageEventSubscriptionName("MSG_協助處理");
        Execution execution = executionQuery.singleResult();
        runtimeService.messageEventReceived("MSG_協助處理", execution.getId());



ExecutionQuery查詢對象查詢流程執行對象(Execution,與ProcessInstance是多對一的關係。引擎遇到消息邊界事件的任務時建立一個執行對象監聽消息事件的觸發動做。

三、信號邊界事件與消息邊界事件的機制類似


<signal id="S_HELP" name="S_協助處理"></signal>
  <process id="signalBoundaryEvent" name="Signal Boundary Event" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="審覈文件"></userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <boundaryEvent id="boundarysignal1" name="Signal" attachedToRef="usertask1" cancelActivity="true">
      <signalEventDefinition signalRef="S_HELP"></signalEventDefinition>
    </boundaryEvent>
    <userTask id="usertask2" name="協助處理"></userTask>
    <sequenceFlow id="flow2" sourceRef="boundarysignal1" targetRef="usertask2"></sequenceFlow>
    <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask1"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow4" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
  </process>



4、中間事件

分爲捕獲型和拋出型兩種,定義以下:


<process id="throwAlert" name="My process" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <intermediateThrowEvent id="signalintermediatethrowevent1" name="SignalThrowEvent">
      <signalEventDefinition signalRef="alert"></signalEventDefinition>
    </intermediateThrowEvent>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="signalintermediatethrowevent1"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow2" sourceRef="signalintermediatethrowevent1" targetRef="endevent1"></sequenceFlow>
    <textAnnotation id="textannotation1">
      <text>拋出alert信號</text>
    </textAnnotation>
    <association id="association1" sourceRef="textannotation1" targetRef="signalintermediatethrowevent1"></association>
  </process>



<signal id="alert" name="alert"></signal>
  <signal id="abort" name="abort"></signal>
  <process id="catchSignal" name="My process" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="附加邊界用戶任務"></userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <intermediateCatchEvent id="signalintermediatecatchevent1" name="SignalCatchEvent">
      <signalEventDefinition signalRef="abort"></signalEventDefinition>
    </intermediateCatchEvent>
    <sequenceFlow id="flow2" sourceRef="startevent1" targetRef="signalintermediatecatchevent1"></sequenceFlow>
    <userTask id="usertask2" name="被中間信號拋出事件觸發"></userTask>
    <sequenceFlow id="flow3" name="捕獲abort信號" sourceRef="signalintermediatecatchevent1" targetRef="usertask2"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow4" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow>
    <sequenceFlow id="flow5" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
    <boundaryEvent id="boundarysignal1" name="Signal" attachedToRef="usertask1" cancelActivity="true">
      <signalEventDefinition signalRef="alert"></signalEventDefinition>
    </boundaryEvent>
    <sequenceFlow id="flow6" name="捕獲alert信號" sourceRef="boundarysignal1" targetRef="usertask3"></sequenceFlow>
    <userTask id="usertask3" name="經過信號邊界事件觸發"></userTask>
    <sequenceFlow id="flow7" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
  </process>
相關文章
相關標籤/搜索