`
redxun
  • 浏览: 298382 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

如何实现Activiti的分支条件的自定义配置

 
阅读更多

一、Activiti的流程分支条件的局限

Activiti的流程分支条件目前是采用脚本判断方式,并且需要在流程定义中进行分支条件的设定,如下图所示:



 

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

 

 

从上面的定义可以看到,流程的分支条件存在以下两个致命的局限性:

1.分支条件需要在流程定义(XML)中设定,这要求流程定义必须由开发人员来设计及编写

2.分支条件比较简单,一般为boolean表达式,表达式里的为单变量的判断处理。

 

以上两个局限性限制了流程的分支判断处理必须由开发人员来设定,而国内的大部分的流程应用都要求是普通的业务人员即可处理,或者是由有一定计算机基础的人员来设置处理。这要求我们对流程的条件设置提出了更高的要求,上一节我们通过修改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!=null){
    			//取得分支条件列表
    			JsonNode configs=configsNode.get("conditions");
    			if(configs!=null){
    				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==null) continue;
    					
    					Element conditionExpress=(Element)seqFlow.selectSingleNode("bpm:conditionExpression");
    					if(conditionExpress==null){
    						conditionExpress=seqFlow.addElement("conditionExpression");
    	  					conditionExpress.addAttribute("xsi:type", "tFormalExpression");
    					}else{
    						conditionExpress.clearContent();
    					}
    					conditionExpress.addCDATA(tmpCondition);
	  					
    				}
    			}
    		}
			//修改流程定义的XML,并且清空该流程定义的缓存
    		actRepService.doModifyXmlAndClearCache(bpmDef.getActDefId(),bpmDef.getActDepId(), doc.asXML());

 

 

 

【说明】

 

1.基于这种方式容易出错,因为流程的分支条件写回流程定义的XML是比较容易出问题的,同时不清楚人员填写什么条件回XML文件中。

2.对于Jsaas中的一个流程定义可用于多个流程解决方案中使用配置不同的条件不太适合,因为一个流程定义是一样,但可能会分支的条件设置不一样。

 

 

基于以上的要求,为此我们对Activiti进行扩展,以使得我们可以允许流程引擎在分支判断处理中,执行我们的条件设置,其原理如下:



 

当流程引擎跳至分支条件判断处理时,可以让它执行我们的脚本设置条件,条件满足时,则跳至我们的设置的目标节点,从而实现干预流程引擎本身的执行方式,为了不影响Activiti的原的运行机制,我们还是保留其旧的执行判断方式。

 

 

二、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;
	}
}

 

三、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"/>

 

 

通过以上方式扩展后,节点的分支设置可以把条件表达式写成以下方式,同时条件存于流程定义的外部表中:


 

  • 大小: 19.1 KB
  • 大小: 39.5 KB
  • 大小: 39.9 KB
分享到:
评论
1 楼 nihat520 2017-05-15  
里面有些包没有 :

相关推荐

    activiti6.0工作流配置(中文)

    4. 表单与表单字段:使用Form Service进行表单数据的处理,结合自定义表单实现业务数据的录入和展示。 五、示例代码分析 `activiti-app`文件夹中的示例应用,通常包含了一个完整的Activiti集成实例,可以从中学习...

    activiti的说明文档

    - **内部实现类**: Activiti内部通过一系列的类来实现其功能,包括流程引擎、服务层、持久化层等。 #### 二、开始学习 **一分钟入门**: - **安装Activiti**: 下载Activiti并按照指南完成安装。 - **安装Activiti...

    Activiti5 用户手册 中文版

    2. 表单设计:流程中的表单可以通过 Activiti 表单引擎自定义,支持静态表单和动态表单,提高用户体验。 四、Activiti 运行与控制 1. 流程实例管理:用户可以启动、暂停、继续、终止流程实例,对流程实例进行跟踪和...

    Activiti官方唯一推荐中文书籍——《Activiti实战》示例源码

    10. **分布式部署与集群支持**:对于企业级应用,Activiti支持分布式部署和集群环境,源码中可能包含关于这部分的配置和实现。 通过对《Activiti实战》示例源码的深入研究,开发者不仅可以掌握Activiti的基本用法,...

    activiti用户指南.zip

    6. 自定义扩展:Activiti 提供了丰富的插件和扩展机制,允许开发者根据需求进行定制。 三、Activiti 安装与配置 安装 Activiti 包括下载最新版本的 Activiti 程序,配置数据库连接,以及启动 Activiti 引擎。在配置...

    Activiti 实战工具包

    总的来说,"Activiti 实战工具包"是一个完整的开发套件,涵盖了从流程设计、环境配置到实际应用开发的全过程。对于想要学习和使用Activiti的人来说,这是一个非常宝贵的资源,能够帮助他们快速上手并深入理解...

    activiti-app6.0中文版.zip

    1. **Activiti 工作流引擎**:Activiti 是基于BPMN 2.0标准的,这意味着它能够支持各种复杂的业务流程,包括并行分支、循环、事件触发等。它还支持自定义工作流行为,可以通过编写JavaDelegate或者使用表达式语言...

    Activiti 7.3.0源代码

    【描述】中的"Activiti 7.3.0源代码"强调了这是该版本的具体实现代码,包含了所有的类、接口、方法以及相关的配置文件。通过阅读和分析源代码,开发者能够了解到如何构建和运行工作流引擎,以及如何设计和执行复杂的...

    activiti原型和代码

    例如,用户任务(User Task)代表需要人工干预的操作,而 Exclusive Gateway(排他网关)则用于根据条件分支流程。 进入“代码”部分,这部分将展示如何在实际项目中使用 Activiti。这可能包括以下几个方面: 1. *...

    Activiti工作流的快速应用源码

    6. 自定义行为:研究如何扩展Activiti,实现自定义的用户任务行为、服务任务行为等。 四、源码实践 1. 创建简单的流程实例:从源码中学习如何创建一个基本的请假审批流程,并运行它。 2. 集成表单:利用源码实现与...

    activiti6.0 中文api

    10. **事件(Events)**:Activiti提供了丰富的事件机制,如流程启动、结束、任务完成等,开发者可以通过API订阅这些事件来实现自定义功能。 以上只是Activiti 6.0中文API的一部分核心概念,实际使用时,开发者还...

    activiti 6.x 全帮助文档(官方版本)

    Activiti 是一个开源的工作流引擎,它被广泛用于企业级应用中实现业务流程自动化。6.x 版本是其一个重要迭代,提供了许多增强的功能和改进。这个“activiti 6.x 全帮助文档(官方版本)”包含了全面的指南、教程和...

    Activiti-7.1.17.zip

    10. **用户和组管理**:理解如何配置 Activiti 的用户和组,设置权限,以实现不同角色对流程的访问和操作。 通过 "Activiti-7.1.17.zip" 文件,你可以深入研究上述知识点,并结合源码和示例项目进行实践。这将有助...

    工作流activiti简单demo

    - 安装和配置:首先,需要在项目中集成Activiti库,设置数据源和配置Activiti引擎。 - 创建流程定义:使用Activiti Modeler或其他支持BPMN 2.0的工具绘制流程图,并保存为XML文件。 - 启动流程实例:通过调用引擎...

    activiti-5.17.0

    工作流控制流程的流转,包括条件分支、并发分支、子流程等复杂逻辑。 4. **表单与表单引擎**:Activiti 可以集成表单,用于数据收集和展示。表单可以是静态HTML,也可以通过Form模型动态生成。表单引擎提供了表单与...

    Activiti 5.8 用户指南

    - 详细介绍如何在 Eclipse IDE 中配置 Activiti 项目,包括必要的插件安装和环境配置。 ##### 2.6 数据库查看 - 指导如何查看 Activiti 在运行过程中的数据库状态。 ##### 2.7 数据库表命名 - 解释 Activiti 5.8 ...

    activiti-5.13.

    1. **流程定义**:Activiti 提供了基于 BPMN 2.0 标准的流程定义语言,这使得流程图可以清晰地表示各种业务流程,包括任务、决策点、分支和合并等。 2. **工作流引擎**:它是 Activiti 的核心,负责执行流程实例,...

    activiti动态创建路径

    网关(Gateway)是实现动态路径的关键,如 Exclusive Gateway(排他网关)可以根据条件分支流程,Parallel Gateway(并行网关)则可以并行处理多个任务。在源码层面,这些元素的实例化和配置可以通过API进行动态构建...

    activiti web 例子 源码

    在示例中,你可能会看到如何集成自定义服务,以实现流程中的业务逻辑。 5. **表单(Forms)**:Activiti支持与Web表单的集成,以便在处理任务时收集或显示数据。源码中可能包含如何配置和使用这些表单的示例。 6. ...

Global site tag (gtag.js) - Google Analytics