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

博客分类:

  • Activiti
  • Java SaaS

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

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

Xml代码  
  1. <sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="theTask1">
  2. <conditionExpression xsi:type="tFormalExpression">${input == 1}</conditionExpression>
  3. </sequenceFlow>
  4. <sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="theTask2">
  5. <conditionExpression xsi:type="tFormalExpression">${input == 2}</conditionExpression>
  6. </sequenceFlow>
  7. <sequenceFlow id="flow4" sourceRef="exclusiveGw" targetRef="theTask3">
  8. <conditionExpression xsi:type="tFormalExpression">${input == 3}</conditionExpression>
  9. </sequenceFlow>

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

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

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

以上两个局限性限制了流程的分支判断处理必须由开发人员来设定,而国内的大部分的流程应用都要求是普通的业务人员即可处理,或者是由有一定计算机基础的人员来设置处理。这要求我们对流程的条件设置提出了更高的要求,上一节我们通过修改Activiti的流程定义的XML中的分支条件表达式,同时刷新流程定义的引擎缓存,如下的代码就是基于这种方式:

Java代码  
  1. JsonNode jsonObject=objectMapper.readTree(configJson);
  2. JsonNode configsNode=jsonObject.get("configs");
  3. BpmSolution bpmSolution=bpmSolutionManager.get(solId);
  4. BpmDef bpmDef=bpmDefManager.getLatestBpmByKey(bpmSolution.getDefKey(), ContextUtil.getCurrentTenantId());
  5. ActProcessDef processDef=actRepService.getProcessDef(bpmDef.getActDefId());
  6. String processDefXml=actRepService.getBpmnXmlByDeployId(bpmDef.getActDepId());
  7. System.out.println("xml:"+processDefXml);
  8. ActNodeDef sourceNode=processDef.getNodesMap().get(nodeId);
  9. ByteArrayInputStream is=new ByteArrayInputStream(processDefXml.getBytes());
  10. Map<String,String> map = new HashMap<String,String>();
  11. map.put("bpm","http://www.omg.org/spec/BPMN/20100524/MODEL");
  12. map.put("xsi","http://www.omg.org/spec/BPMN/20100524/MODEL");
  13. SAXReader saxReader = new SAXReader();
  14. saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
  15. Document doc = saxReader.read(is);
  16. //Document doc=Dom4jUtil.load(is, "UTF-8");
  17. Element rootEl=doc.getRootElement();
  18. if(configsNode!=){
  19. //取得分支条件列表
  20. JsonNode configs=configsNode.get("conditions");
  21. if(configs!=){
  22. Iterator<JsonNode> it=configs.elements();
  23. while(it.hasNext()){
  24. ObjectNode config=(ObjectNode)it.next();
  25. String tmpNodeId=config.get("nodeId").textValue();
  26. String tmpCondition=config.get("condition").textValue();
  27. Element seqFlow=(Element)rootEl.selectSingleNode("/bpm:definitions/bpm:process/bpm:sequenceFlow[@sourceRef='"
  28. +sourceNode.getNodeId()+"' and @targetRef='"+tmpNodeId+"']");
  29. if(seqFlow==) continue;
  30. Element conditionExpress=(Element)seqFlow.selectSingleNode("bpm:conditionExpression");
  31. if(conditionExpress==){
  32. conditionExpress=seqFlow.addElement("conditionExpression");
  33. conditionExpress.addAttribute("xsi:type", "tFormalExpression");
  34. }else{
  35. conditionExpress.clearContent();
  36. }
  37. conditionExpress.addCDATA(tmpCondition);
  38. }
  39. }
  40. }
  41. //修改流程定义的XML,并且清空该流程定义的缓存
  42. actRepService.doModifyXmlAndClearCache(bpmDef.getActDefId(),bpmDef.getActDepId(), doc.asXML());

【说明】

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

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

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

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

二、Activiti的扩展点

Activiti的流程扩展是比较灵活的,我们通过改写这个ExclusiveGateway的节点的行为方法即可,其实现方法如下:

Java代码  
  1. package com.redxun.bpm.activiti.ext;
  2. import java.util.Iterator;
  3. import javax.annotation.Resource;
  4. import org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior;
  5. import org.activiti.engine.impl.pvm.PvmTransition;
  6. import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
  7. import org.apache.commons.lang.StringUtils;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import com.redxun.bpm.core.entity.config.ExclusiveGatewayConfig;
  11. import com.redxun.bpm.core.entity.config.NodeExecuteScript;
  12. import com.redxun.bpm.core.manager.BpmNodeSetManager;
  13. import com.redxun.core.script.GroovyEngine;
  14. import com.sun.star.uno.RuntimeException;
  15. /**
  16. * 对网关的条件判断,优先使用扩展的配置
  17. * @author keitch
  18. *
  19. */
  20. @SuppressWarnings("serial")
  21. public class ExclusiveGatewayActivityBehaviorExt extends ExclusiveGatewayActivityBehavior{
  22. protected static Logger log = LoggerFactory.getLogger(ExclusiveGatewayActivityBehaviorExt.class);
  23. //节点的设置管理器
  24. @Resource
  25. BpmNodeSetManager bpmNodeSetManager;
  26. //脚本引擎
  27. @Resource GroovyEngine groovyEngine;
  28. @Override
  29. protected void leave(ActivityExecution execution) {
  30. log.debug("enter ExclusiveGatewayActivityBehaviorExt=======================");
  31. if (log.isDebugEnabled()) {
  32. log.debug("Leaving activity '{}'", execution.getActivity().getId());
  33. }
  34. String solId=(String)execution.getVariable("solId");
  35. String nodeId=execution.getActivity().getId();
  36. log.debug("solid is {} and nodeId is {}",solId,nodeId);
  37. if(StringUtils.isNotEmpty(solId)&& StringUtils.isNotBlank(nodeId)){
  38. ExclusiveGatewayConfig configs=bpmNodeSetManager.getExclusiveGatewayConfig(solId, nodeId);
  39. for(NodeExecuteScript script:configs.getConditions()){
  40. String destNodeId=script.getNodeId();
  41. String condition=script.getCondition();
  42. log.debug("dest node:{}, condition is {}",destNodeId,condition);
  43. //执行脚本引擎
  44. Object boolVal=groovyEngine.executeScripts(condition, execution.getVariables());
  45. if(boolVal instanceof Boolean){
  46. Boolean returnVal=(Boolean)boolVal;//符合条件
  47. if(returnVal==true){
  48. //找到符合条件的目标节点并且进行跳转
  49. Iterator<PvmTransition> transitionIterator = execution.getActivity().getOutgoingTransitions().iterator();
  50. while (transitionIterator.hasNext()) {
  51. PvmTransition seqFlow = transitionIterator.next();
  52. if(destNodeId.equals(seqFlow.getDestination().getId())){
  53. execution.take(seqFlow);
  54. return;
  55. }
  56. }
  57. }
  58. }else{
  59. throw new RuntimeException("表达式:\n "+condition+"\n返回值不为布尔值(true or false)");
  60. }
  61. }
  62. }
  63. //执行父类的写法,以使其还是可以支持旧式的在跳出线上写条件的做法
  64. super.leave(execution);
  65. }
  66. }

我们通过继续改写了这个分支节点的跳出机制,并且通过脚本引擎来执行其条件分支的判断处理,但流程引擎并不了解我们扩展的类,这时我们需要配置Activiti流程引擎的行为动作工厂类,如下所示:

Java代码  
  1. package com.redxun.bpm.activiti.ext;
  2. import org.activiti.bpmn.model.ExclusiveGateway;
  3. import org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior;
  4. import org.activiti.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory;
  5. /**
  6. * 扩展缺省的流程节点默认工厂类,实现对Activiti节点的执行的默认行为的更改
  7. * @author keitch
  8. *
  9. */
  10. public class ActivityBehaviorFactoryExt extends DefaultActivityBehaviorFactory {
  11. private ExclusiveGatewayActivityBehaviorExt exclusiveGatewayActivityBehaviorExt;
  12. /**
  13. * 通过Spring容器注入新的分支条件行为执行类
  14. * @param exclusiveGatewayActivityBehaviorExt
  15. */
  16. public void setExclusiveGatewayActivityBehaviorExt(ExclusiveGatewayActivityBehaviorExt exclusiveGatewayActivityBehaviorExt) {
  17. this.exclusiveGatewayActivityBehaviorExt = exclusiveGatewayActivityBehaviorExt;
  18. }
  19. //重写父类中的分支条件行为执行类
  20. @Override
  21. public ExclusiveGatewayActivityBehavior createExclusiveGatewayActivityBehavior(ExclusiveGateway exclusiveGateway) {
  22. return exclusiveGatewayActivityBehaviorExt;
  23. }
  24. }

三、Activiti的Spring配置的更改

在Activiti的流程引擎配置中加入新的流程行为动作执行工厂类。配置如下所示,注意activityBehaviorFactory的属性配置:

Xml代码  
  1. <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
  2. <property name="dataSource" ref="dataSource" />
  3. <property name="transactionManager" ref="transactionManager" />
  4. <property name="databaseSchemaUpdate" value="true" />
  5. <property name="jobExecutorActivate" value="false" />
  6. <property name="enableDatabaseEventLogging" value="false" />
  7. <property name="databaseType" value="${db.type}" />
  8. <property name="idGenerator" ref="actIdGenerator"/>
  9. <property name="eventListeners">
  10. <list>
  11. <ref bean="globalEventListener"/>
  12. </list>
  13. </property>
  14. <property name="activityFontName" value="宋体"/>
  15. <property name="labelFontName" value="宋体"/>
  16. <!-- 用于更改流程节点的执行行为 -->
  17. <property name="activityBehaviorFactory" ref="activityBehaviorFactoryExt"/>
  18. </bean>
  19. <bean id="activityBehaviorFactoryExt" class="com.redxun.bpm.activiti.ext.ActivityBehaviorFactoryExt">
  20. <property name="exclusiveGatewayActivityBehaviorExt" ref="exclusiveGatewayActivityBehaviorExt"/>
  21. </bean>
  22. <bean id="exclusiveGatewayActivityBehaviorExt" class="com.redxun.bpm.activiti.ext.ExclusiveGatewayActivityBehaviorExt"/>

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

如何实现Activiti的分支条件的自定义配置(转)相关推荐

  1. Matlab编程与数据类型 -- 多分支条件选择语句if/elseif/…/else/end

    本微信图文详细介绍了Matlab中if/elseif/-/else/end多分支条件选择语句.

  2. Matlab编程与数据类型 -- 分支条件选择语句if/end

    本微信图文详细介绍了Matlab中if/end分支条件选择语句.

  3. activiti脚本任务_Activiti中的高级脚本:自定义配置注入

    activiti脚本任务 脚本任务可能是Activiti代码库中"最古老的"类之一,但是我认为它仍然未被许多人使用. (感知到的)缺点当然是性能(解释还是编译),并且从IDE角度来 ...

  4. Activiti中的高级脚本:自定义配置注入

    脚本任务可能是Activiti代码库中"最古老的"类之一,但我认为它仍然未被许多人使用. (可以理解的?)缺点当然是性能(解释还是编译),并且从IDE角度来看支持较少. 但是,好处 ...

  5. php case 多个条件判断语句,Shell case语句(多分支条件判断)

    case 语句和 if-elif-else 语句一样都是多分支条件语句,不过和多分支 if 条件语句不同的是,case 语句只能判断一种条件关系,而 if 语句可以判断多种条件关系. case 语句的 ...

  6. Cocoapods警告 - CocoaPods没有设置项目的基本配置,因为您的项目已经有自定义配置集

    本文翻译自:Cocoapods Warning - CocoaPods did not set the base configuration of your project because becau ...

  7. SpringBoot(2)之自定义配置

    2019独角兽企业重金招聘Python工程师标准>>> 覆盖 Spring Boot 自动配置 使用SpringSecurity <dependency><grou ...

  8. 基于 Spring Security 搭建用户权限系统(二) - 自定义配置

    说明 本文的目的是如何基于 Spring Security 去扩展实现一个基本的用户权限模块, 内容会覆盖到 Spring Security 常用的配置. 文中涉及到的业务代码是不完善的, 甚至会存在 ...

  9. SpringBoot和日志框架:缘由,日志框架的选择,使用,自定义配置,日志框架切换

    日志框架 缘由 如果像我们初学者,想知道代码运行到哪里了,一般都是会System.out.println() 进行输出到控制台查看代码运行的情况,好知道代码错误在哪里 但是大型的系统里面,如果有很多的 ...

最新文章

  1. argparse.ArgumentParser
  2. 携程Apollo(阿波罗)配置中心在.NET Core项目快速集成
  3. css水平垂直居中(绝对定位居中)
  4. 刚参加完阿里Java P6面试归来,6点面试经验总结!(含必考题答案)
  5. java项目添加jar包
  6. Eclipse安装STS插件
  7. 信息奥赛一本通(1231:最小新整数)
  8. HNOI2008 GT考试 (KMP + 矩阵乘法)
  9. “滤镜景点”太坑遭吐槽!小红书致歉:将推出景区踩坑榜
  10. 服务机器人占领智能安防哪些领域?
  11. css选择器([class*= icon-], [class^=icon-] 的区别)
  12. 洛谷 4933 洛谷10月月赛II T2 大师
  13. Mathematics Base - 期望、方差、协方差、相关系数总结
  14. sql 取一个月日期去掉周末的所有日期_LeetCode:SQL
  15. 航信三大系统ICS DCS CRS
  16. 大数据技术与实践实验报告总结_大数据平台搭建实验心得体会
  17. Android Activity 的详细启动过程分析
  18. android 手机2k分辨率,2K屏幕手机有哪些 2016六款2K分辨率手机推荐
  19. 保险丝的常用规格型号及主要参数说明
  20. 倒数日怎么显示在桌面_偏头痛是怎么来的?医生:和这4个因素关系密切,要注意...

热门文章

  1. pythonfromlxml导入html_Python爬虫日记二:使用lxml解析HTML输出对应值
  2. java棋盘覆盖分治法_【单选题】实现棋盘覆盖算法利用的算法是( ) A. 分治法 B. 动态规划法 C. 贪心法 D. 回溯法...
  3. python找不到文件数据_如何用 Python 正确读取资源文件
  4. 怎么打_这比赛怎么能够这样打?
  5. 计算机管理 没有适当的权限,提示没有合适的权限访问怎么办
  6. python的__name__
  7. java icache_java手写多级缓存
  8. e0312 不存在用户定义的_更加灵活的参数校验,Spring-boot自定义参数校验注解
  9. 拆装计算机主机,台式计算机的拆装教案.doc
  10. reStructuredText