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

标签: activiti 分支 条件 | 发表时间:2015-11-17 22:30 | 作者:redxun
出处:http://www.iteye.com

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

 

 

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


 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [activiti 分支 条件] 推荐:

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

- - 企业架构 - ITeye博客
一、Activiti的流程分支条件的局限. Activiti的流程分支条件目前是采用脚本判断方式,并且需要在流程定义中进行分支条件的设定,如下图所示:. 从上面的定义可以看到,流程的分支条件存在以下两个致命的局限性:. 1.分支条件需要在流程定义(XML)中设定,这要求流程定义必须由开发人员来设计及编写.

Activiti用户指南之Activiti的API

- - ITeye博客
 一、流程引擎的API和服务(services).      引擎的API是影响Activiti最常见的一种方法. 我们一开始最关注的中心是ProcessEngine,像之前描述的那样,流程引擎可以被多种方式创建. 从这个流程引擎里面,你能获得各个包含workflow/BPM方法的服务. 流程引擎和这些获得的服务是线程安全的.

Activiti学习笔记

- - 企业架构 - ITeye博客
第一个Activiti的HelloWorld. 获取核心ProcessEngine对象 2. 根据需求,获取对应的服务实例 3. 使用服务方法,做事情 * * @author Administrator * */ public class HelloWorld {. // 加载核心API ProcessEngine.

Activiti工作流demo

- - CSDN博客综合推荐文章
继上篇《 Activiti工作流的环境配置》.        前几篇对Activiti工作流进行了介绍,并讲解了其环境配置. 本篇将会用一个demo来展示Activiti工作流具体的体现,直接上干货.        以HelloWorld程序为例.       首先说一下业务流程,员工张三提交了一个申请,然后由部门经理李四审核,审核通过后再由总经理王五审核,通过则张三申请成功.

Activiti - 设置会签

- - 企业架构 - ITeye博客
前些天在群里聊工作流和Activiti,群里有人分享了自己的工作流引擎开源项目,大伙纷纷问这问那(比如为什么突然自己搞个process engine、有没有eclipse plugin、能不能绘制流程图等等). 现实生活中的工作流程,我们也经常碰到需要会签的情况,支持会签是很必要的. 正好有两个人问道:支持会签吗.

activiti工作流使用

- - 行业应用 - ITeye博客
activiti 开发流程. JBPM 与 Activiti. jBPM项目于2002年3月由Tom Baeyens发起,2003年12月发布1.0版本. 2004年10月18日,发布了2.0版本,并在同一天加入了JBoss. 2011 年 jBPM的创建者Tom Baeyens离开JBoss了, 他的离开产生了两个结果:.

ACTIVITI 学习笔记 - 监听

- - 企业架构 - ITeye博客
ACTIVITI 学习笔记 - 监听. 所有分发的事件都是org.activiti.engine.delegate.event.ActivitiEvent的子类. 监听器监听的流程引擎已经创建完毕,并准备好接受API调用. 监听器监听的流程引擎已经关闭,不再接受API调用. 创建了一个新实体,初始化也完成了.

Activiti安装配置(转)

- - 企业架构 - ITeye博客
原文地址:http://blog.csdn.net/zhang_xinxiu/article/details/38655311. 有一段时间没有更新文章了,虽然有一直在写文章,可是一直没有更新到博客内,这段时间写的文章大多还是以技术为主. 接下来的系列文章将会来讨论企业工作流的开发,主要是来研究开源工作流Activiti的使用.

Activiti入门篇之二 Spring 与Activiti的入门整合

- - 行业应用 - ITeye博客
Activiti相对Jbpm来说,与Spring整合更加完美,具体可见本文的详细介绍. 1.     Maven的环境任务,请参考第一篇 (Activiti入门篇—Maven的环境准备). 2.     Activiti的Eclipse插件安装.               插件更新地址:http://activiti.org/designer/update/.

整合activiti在线流程设计器(ACTIVITI-MODELER 5.18.0)

- - 企业架构 - ITeye博客
一直以来都是从事大量的工作流相关的项目,用过很多商用的工作流产品,包括国内与国外的,尽管商用的工作产品在UI操作上比较人性化,但个人用户觉得,这东西只需要一些初级用户,对于我们一直在为一些高级的客户提供一些专业的数据整合、流程梳理、系统间的数据穿透时,这些系统因为不开源,给项目的实施带来巨大的风险,在一些项目栽过跟头后,我更偏向于使用开源的平台了.