activiti 5.21工做流规则引擎扩展(businessRuleTask)

背景介绍:java

公司有本身的规则引擎配置平台,执行核心为drools,配置后生成规则脚本,存入数据库,执行的时候调用drools的Api,关键代码:
        StatelessSession statelessSession = ruleBase.newStatelessSession();
        statelessSession.setGlobal("externalConditionResult", true);
        statelessSession.execute(list);数据库

之前一直是在后台硬编码调用每个规则。缓存

使用activiti5工做流引擎,节点使用上只是用了用户任务和自动任务,封装在两个方面:1.流程启动,任务获取,完成任务,也就是对这几个API的封装,同时引入了流程业务关联表。2。在流程节点分配这块使用咱们系统的岗位或者角色,拥有该岗位/角色的人均可以处理这个任务,分配到岗位/角色这块是经过规则配置的,后台也是硬编码的,如今平台升级了之后,有这么个需求,就是流程编辑器上能够直接配置规则节点,这样的话,用户能够直接修改流程图,选择要调用的规则session

activiti5自己是支持规则节点的,网上查一查,也能查到一些资料,总结一下:less

  1. 引入流程规则执行所需JAR包:
  2. 抒写规则脚本,在页面上部署工做流的时候,同时选择要部署的规则脚本,添加到工做的act_ge_bytearray表,
  3. 配置流程配置文件:

<property name="customPostDeployers">
<list>
<bean class="org.activiti.engine.impl.rules.RulesDeployer" />
</list>
</property>编辑器

就是说执行到规则节点的时候,以.drl结尾的drool脚本会交给RulesDeploer部署,加载到内存,供规则节点执行器执行。ui

4。在流程编辑器上的规则节点添加规则名,输入变量,输出变量,继承JavaDelegate获取输出变量,或者直接在流程图上根据流程变量zhi配置节点的走向编码

这样看就完活了,可是实际执行咱们的规则的时候怎么调试到报错,这方面没有完善的文档,只能看源码了只能看源代码了。debug

首先咱们的规则执行方式是无状态的StatelessKnowledgeSession,结果变量就是一个实体类,是以在内存中更新全局变量的方式返回的。调试

规则节点的执行是BusinessRuleTaskActivityBehavior来处理的

 KnowledgeBase knowledgeBase =RulesHelper.findKnowledgeBaseByDeploymentId(deploymentId); 
 StatefulKnowledgeSession ksession = knowledgeBase.newStatefulKnowledgeSession();

1.经过流程实例部署ID,在内存中找到以.drl结尾的脚本名称,若是没有,从新查询act_ge_bytearray表,加载到内存,添加到知识库.

2.获取规则节点上配置的输入输出变量,规则名,若是不配置规则名,则所有执行,执行方式是经过有状态StatefulKnowledgeSession的fireAllRules来执行规则脚本,其中一个关键点是咱们的规则脚本里面不少个规则名,总不能一个一个写到编辑器上面吧,重写执行源码,发现工做流实现了drools的规则匹配,只需修改咱们的规则名已相同的名字结尾,流程编辑器上配置这个相同的名字,就能够所有执行了,对于结果变量,drools不支持在他的工组内存中更新全局变量,若是须要这样作,就要调用ksession.setGlobal("ruleResultBase", obj);将全局变量set到他的工做内存,获取结果变量是一个集合,规则那一套基本不会改动,那怎么办,直接修改改这个类源码好了,修改成获取当个实体(不是一个好方法,后面会说到),就这样把源码拿出来改了,能够部署了,调试也经过了。。。。,

第一个问题解决了:经过改动BusinessRuleTaskActivityBehavior源码,工做流能够调用咱们的规则脚本,能够和规则联动了。

public void execute(ActivityExecution execution) throws Exception {
    PvmProcessDefinition processDefinition = execution.getActivity().getProcessDefinition();
    String deploymentId = processDefinition.getDeploymentId();
    
    KnowledgeBase knowledgeBase = RulesHelper.findKnowledgeBaseByDeploymentId(deploymentId); 
    StatefulKnowledgeSession ksession = knowledgeBase.newStatefulKnowledgeSession();


    if (variablesInputExpressions != null) {
      Iterator<Expression> itVariable = variablesInputExpressions.iterator();
      while (itVariable.hasNext()) {
        Expression variable = itVariable.next();
        Object obj=variable.getValue(execution);
        if(obj instanceof IRuleResultBase){
            //设置全局变量
        	ksession.setGlobal("ruleResultBase", obj);
        }
        ksession.insert(variable.getValue(execution));
      }
    }

    if (!rulesExpressions.isEmpty()) {
      RulesAgendaFilter filter = new RulesAgendaFilter();
      Iterator<Expression> itRuleNames = rulesExpressions.iterator();
      while (itRuleNames.hasNext()) {
        Expression ruleName = itRuleNames.next();
        filter.addSuffic(ruleName.getValue(execution).toString());
      }
      filter.setAccept(!exclude);
      ksession.fireAllRules(filter);
      
    } else {
      ksession.fireAllRules();
    }
    //客户化 结果bean
   /* Collection<Object> ruleOutputObjects = ksession.getObjects();
    if (ruleOutputObjects != null && !ruleOutputObjects.isEmpty()) {
      Collection<Object> outputVariables = new ArrayList<Object>();
      for (Object object : ruleOutputObjects) {
        outputVariables.add(object);
      }
      execution.setVariable(resultVariable, outputVariables);
    }*/
   
    /*ruleResultBase=(RuleResultBase) ksession.getGlobal("ruleResultBase");
    Collection<Object> outputVariables = new ArrayList<Object>();
    outputVariables.add(ruleResultBase);
    execution.setVariable(resultVariable, ruleResultBase);*/
    execution.setVariable(resultVariable, ksession.getGlobal("ruleResultBase"));
    ksession.dispose();
    leave(execution);
  }

 

可是有几个问题,规则编辑以后难道要从新部署流程吗?看流程是如何加载规则的

public class RulesHelper {

  public static KnowledgeBase findKnowledgeBaseByDeploymentId(String deploymentId) {
    DeploymentCache<Object> knowledgeBaseCache = Context
      .getProcessEngineConfiguration()
      .getDeploymentManager()
      .getKnowledgeBaseCache();
  
    KnowledgeBase knowledgeBase = (KnowledgeBase) knowledgeBaseCache.get(deploymentId);
    if (knowledgeBase==null) {
      DeploymentEntity deployment = Context
        .getCommandContext()
        .getDeploymentEntityManager()
        .findDeploymentById(deploymentId);
      if (deployment==null) {
        throw new ActivitiObjectNotFoundException("no deployment with id "+deploymentId, Deployment.class);
      }
      Context
        .getProcessEngineConfiguration()
        .getDeploymentManager()
        .deploy(deployment);
      knowledgeBase = (KnowledgeBase) knowledgeBaseCache.get(deploymentId);
      if (knowledgeBase==null) {
        throw new ActivitiException("deployment "+deploymentId+" doesn't contain any rules");
      }
    }
    return knowledgeBase;
  }
}

发现上面代码从缓存中取KnowledgeBase, if (knowledgeBase==null),那么根据部署id,去数据库查询,而后交给Deployer来部署,这是一个接口:

public interface Deployer {

  void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings);
}

对于规则的部署,activiti的实现类为前面提到的,配置文件中配置的RulesDeployer

public class RulesDeployer implements Deployer {
  
  private static final Logger log = LoggerFactory.getLogger(RulesDeployer.class);

  public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) {
    log.debug("Processing deployment {}", deployment.getName());
    
    KnowledgeBuilder knowledgeBuilder = null;

    DeploymentManager deploymentManager = Context
      .getProcessEngineConfiguration()
      .getDeploymentManager();
    
    Map<String, ResourceEntity> resources = deployment.getResources();
    for (String resourceName : resources.keySet()) {
      log.info("Processing resource {}", resourceName);
      if (resourceName.endsWith(".drl")) { // is only parsing .drls sufficient? what about other rule dsl's? (@see ResourceType)
        if (knowledgeBuilder==null) {
          knowledgeBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        }
     
        ResourceEntity resourceEntity = resources.get(resourceName);
        byte[] resourceBytes = resourceEntity.getBytes();
        Resource droolsResource = ResourceFactory.newByteArrayResource(resourceBytes);
       
        knowledgeBuilder.add(droolsResource, ResourceType.DRL);
        
      }
    }
    
    if (knowledgeBuilder!=null) {
      KnowledgeBase knowledgeBase = knowledgeBuilder.newKnowledgeBase();
      deploymentManager.getKnowledgeBaseCache().add(deployment.getId(), knowledgeBase);
    }
  }
}

从这里能够知道,文件必须是以.drl结尾,所谓部署就是将规则脚本添加到知识库,用部署ID,做为key,KnowledgeBase做为value,加载到本地缓存,那么解决办法以下:

  • 从新部署流程,带着更新后的规则脚本
  • 直接修改此类,设置一个监听器,时时更新缓存
  • 这个类是实现的Deployer接口,而且在配置文件中可配置的,那么最好的办法就是,实现咱们本身的类来扩展
相关文章
相关标签/搜索