1、Activiti的流程分支條件的侷限java
Activiti的流程分支條件目前是採用腳本判斷方式,而且須要在流程定義中進行分支條件的設定,以下圖所示:node
- <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>
從上面的定義能夠看到,流程的分支條件存在如下兩個致命的侷限性:spring
1.分支條件須要在流程定義(XML)中設定,這要求流程定義必須由開發人員來設計及編寫apache
2.分支條件比較簡單,通常爲boolean表達式,表達式裏的爲單變量的判斷處理。json
以上兩個侷限性限制了流程的分支判斷處理必須由開發人員來設定,而國內的大部分的流程應用都要求是普通的業務人員便可處理,或者是由有必定計算機基礎的人員來設置處理。這要求咱們對流程的條件設置提出了更高的要求,上一節咱們經過修改Activiti的流程定義的XML中的分支條件表達式,同時刷新流程定義的引擎緩存,以下的代碼就是基於這種方式:緩存
- JsonNode jsonObject=objectMapper.readTree(configJson);
- JsonNode configsNode=jsonObject.get("configs");
- BpmSolution bpmSolution=bpmSolutionManager.get(solId);
- BpmDef bpmDef=bpmDefManager.getLatestBpmByKey(bpmSolution.getDefKey(), ContextUtil.getCurrentTenantId());
- ActProcessDef processDef=actRepService.getProcessDef(bpmDef.getActDefId());
- String processDefXml=actRepService.getBpmnXmlByDeployId(bpmDef.getActDepId());
- System.out.println("xml:"+processDefXml);
- ActNodeDef sourceNode=processDef.getNodesMap().get(nodeId);
- ByteArrayInputStream is=new ByteArrayInputStream(processDefXml.getBytes());
- Map<String,String> map = new HashMap<String,String>();
- map.put("bpm","http://www.omg.org/spec/BPMN/20100524/MODEL");
- map.put("xsi","http://www.omg.org/spec/BPMN/20100524/MODEL");
- SAXReader saxReader = new SAXReader();
- saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
- Document doc = saxReader.read(is);
- //Document doc=Dom4jUtil.load(is, "UTF-8");
- Element rootEl=doc.getRootElement();
- if(configsNode!=){
- //取得分支條件列表
- JsonNode configs=configsNode.get("conditions");
- if(configs!=){
- Iterator<JsonNode> it=configs.elements();
- while(it.hasNext()){
- ObjectNode config=(ObjectNode)it.next();
- String tmpNodeId=config.get("nodeId").textValue();
- String tmpCondition=config.get("condition").textValue();
- Element seqFlow=(Element)rootEl.selectSingleNode("/bpm:definitions/bpm:process/bpm:sequenceFlow[@sourceRef='"
- +sourceNode.getNodeId()+"' and @targetRef='"+tmpNodeId+"']");
- if(seqFlow==) continue;
- Element conditionExpress=(Element)seqFlow.selectSingleNode("bpm:conditionExpression");
- if(conditionExpress==){
- conditionExpress=seqFlow.addElement("conditionExpression");
- conditionExpress.addAttribute("xsi:type", "tFormalExpression");
- }else{
- conditionExpress.clearContent();
- }
- conditionExpress.addCDATA(tmpCondition);
- }
- }
- }
- //修改流程定義的XML,而且清空該流程定義的緩存
- actRepService.doModifyXmlAndClearCache(bpmDef.getActDefId(),bpmDef.getActDepId(), doc.asXML());
【說明】app
1.基於這種方式容易出錯,由於流程的分支條件寫回流程定義的XML是比較容易出問題的,同時不清楚人員填寫什麼條件回XML文件中。ide
2.對於Jsaas中的一個流程定義可用於多個流程解決方案中使用配置不一樣的條件不太適合,由於一個流程定義是同樣,但可能會分支的條件設置不同。this
基於以上的要求,爲此咱們對Activiti進行擴展,以使得咱們能夠容許流程引擎在分支判斷處理中,執行咱們的條件設置,其原理以下:spa
當流程引擎跳至分支條件判斷處理時,可讓它執行咱們的腳本設置條件,條件知足時,則跳至咱們的設置的目標節點,從而實現干預流程引擎自己的執行方式,爲了避免影響Activiti的原的運行機制,咱們仍是保留其舊的執行判斷方式。
2、Activiti的擴展點
Activiti的流程擴展是比較靈活的,咱們經過改寫這個ExclusiveGateway的節點的行爲方法便可,其實現方法以下:
- package com.redxun.bpm.activiti.ext;
- import java.util.Iterator;
- import javax.annotation.Resource;
- import org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior;
- import org.activiti.engine.impl.pvm.PvmTransition;
- import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
- import org.apache.commons.lang.StringUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.redxun.bpm.core.entity.config.ExclusiveGatewayConfig;
- import com.redxun.bpm.core.entity.config.NodeExecuteScript;
- import com.redxun.bpm.core.manager.BpmNodeSetManager;
- import com.redxun.core.script.GroovyEngine;
- import com.sun.star.uno.RuntimeException;
- /**
- * 對網關的條件判斷,優先使用擴展的配置
- * @author keitch
- *
- */
- @SuppressWarnings("serial")
- public class ExclusiveGatewayActivityBehaviorExt extends ExclusiveGatewayActivityBehavior{
- protected static Logger log = LoggerFactory.getLogger(ExclusiveGatewayActivityBehaviorExt.class);
- //節點的設置管理器
- @Resource
- BpmNodeSetManager bpmNodeSetManager;
- //腳本引擎
- @Resource GroovyEngine groovyEngine;
- @Override
- protected void leave(ActivityExecution execution) {
- log.debug("enter ExclusiveGatewayActivityBehaviorExt=======================");
- if (log.isDebugEnabled()) {
- log.debug("Leaving activity '{}'", execution.getActivity().getId());
- }
- String solId=(String)execution.getVariable("solId");
- String nodeId=execution.getActivity().getId();
- log.debug("solid is {} and nodeId is {}",solId,nodeId);
- if(StringUtils.isNotEmpty(solId)&& StringUtils.isNotBlank(nodeId)){
- ExclusiveGatewayConfig configs=bpmNodeSetManager.getExclusiveGatewayConfig(solId, nodeId);
- for(NodeExecuteScript script:configs.getConditions()){
- String destNodeId=script.getNodeId();
- String condition=script.getCondition();
- log.debug("dest node:{}, condition is {}",destNodeId,condition);
- //執行腳本引擎
- Object boolVal=groovyEngine.executeScripts(condition, execution.getVariables());
- if(boolVal instanceof Boolean){
- Boolean returnVal=(Boolean)boolVal;//符合條件
- if(returnVal==true){
- //找到符合條件的目標節點而且進行跳轉
- Iterator<PvmTransition> transitionIterator = execution.getActivity().getOutgoingTransitions().iterator();
- while (transitionIterator.hasNext()) {
- PvmTransition seqFlow = transitionIterator.next();
- if(destNodeId.equals(seqFlow.getDestination().getId())){
- execution.take(seqFlow);
- return;
- }
- }
- }
- }else{
- throw new RuntimeException("表達式:\n "+condition+"\n返回值不爲布爾值(true or false)");
- }
- }
- }
- //執行父類的寫法,以使其仍是能夠支持舊式的在跳出線上寫條件的作法
- super.leave(execution);
- }
- }
咱們經過繼續改寫了這個分支節點的跳出機制,而且經過腳本引擎來執行其條件分支的判斷處理,但流程引擎並不瞭解咱們擴展的類,這時咱們須要配置Activiti流程引擎的行爲動做工廠類,以下所示:
- package com.redxun.bpm.activiti.ext;
- import org.activiti.bpmn.model.ExclusiveGateway;
- import org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior;
- import org.activiti.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory;
- /**
- * 擴展缺省的流程節點默認工廠類,實現對Activiti節點的執行的默認行爲的更改
- * @author keitch
- *
- */
- public class ActivityBehaviorFactoryExt extends DefaultActivityBehaviorFactory {
- private ExclusiveGatewayActivityBehaviorExt exclusiveGatewayActivityBehaviorExt;
- /**
- * 經過Spring容器注入新的分支條件行爲執行類
- * @param exclusiveGatewayActivityBehaviorExt
- */
- public void setExclusiveGatewayActivityBehaviorExt(ExclusiveGatewayActivityBehaviorExt exclusiveGatewayActivityBehaviorExt) {
- this.exclusiveGatewayActivityBehaviorExt = exclusiveGatewayActivityBehaviorExt;
- }
- //重寫父類中的分支條件行爲執行類
- @Override
- public ExclusiveGatewayActivityBehavior createExclusiveGatewayActivityBehavior(ExclusiveGateway exclusiveGateway) {
- return exclusiveGatewayActivityBehaviorExt;
- }
- }
3、Activiti的Spring配置的更改
在Activiti的流程引擎配置中加入新的流程行爲動做執行工廠類。配置以下所示,注意activityBehaviorFactory的屬性配置:
- <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
- <property name="dataSource" ref="dataSource" />
- <property name="transactionManager" ref="transactionManager" />
- <property name="databaseSchemaUpdate" value="true" />
- <property name="jobExecutorActivate" value="false" />
- <property name="enableDatabaseEventLogging" value="false" />
- <property name="databaseType" value="${db.type}" />
- <property name="idGenerator" ref="actIdGenerator"/>
- <property name="eventListeners">
- <list>
- <ref bean="globalEventListener"/>
- </list>
- </property>
- <property name="activityFontName" value="宋體"/>
- <property name="labelFontName" value="宋體"/>
- <!-- 用於更改流程節點的執行行爲 -->
- <property name="activityBehaviorFactory" ref="activityBehaviorFactoryExt"/>
- </bean>
- <bean id="activityBehaviorFactoryExt" class="com.redxun.bpm.activiti.ext.ActivityBehaviorFactoryExt">
- <property name="exclusiveGatewayActivityBehaviorExt" ref="exclusiveGatewayActivityBehaviorExt"/>
- </bean>
- <bean id="exclusiveGatewayActivityBehaviorExt" class="com.redxun.bpm.activiti.ext.ExclusiveGatewayActivityBehaviorExt"/>
經過以上方式擴展後,節點的分支設置能夠把條件表達式寫成如下方式,同時條件存於流程定義的外部表中: