activiti学习(六)——从bpmn文件转化为bpmnModel的过程

前言

本章开始,会对activiti的内部进行较多剖析。activiti的高级使用方法,以及对其进行扩展,都必须建立在对其内部运转比较熟悉的情况下。

在《activiti学习(二)——activiti流程的部署》一文中,提到过activiti会把bpmn文件转化成bpmnModel。本文对将这个过程做介绍。

 

从程序部署寻找流程文档解析入口

我们从部署的实例入手,根据之前的部署命令deploymentBuilder.deploy()跟踪下去,查看DeploymentBuilderImpl.java:

public Deployment deploy() {
    return repositoryService.deploy(this);
}

实际上是调用了repositoryService的deploy方法,接着看RepositoryServiceImpl.java:

public Deployment deploy(DeploymentBuilderImpl deploymentBuilder) {
    return commandExecutor.execute(new DeployCmd<Deployment>(deploymentBuilder));
}

上面是activiti中非常常见的命令模式的使用。我们目前不去分析这一连串繁琐的调用,关注DeployCmd类,实际上最终会调用DeployCmd的execute方法,看看DeployCmd.java:

public Deployment execute(CommandContext commandContext) {
    DeploymentEntity deployment = deploymentBuilder.getDeployment();

    deployment.setDeploymentTime(commandContext.getProcessEngineConfiguration().getClock().getCurrentTime());

    if ( deploymentBuilder.isDuplicateFilterEnabled() ) {
    	......
        //省略设置去重后的处理逻辑
    }

    deployment.setNew(true);
    
    // Save the data
    commandContext
      .getDeploymentEntityManager()
      .insertDeployment(deployment);
    
    if (commandContext.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
	    commandContext.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
	    		ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_CREATED, deployment));
    }
    
    // Deployment settings
    Map<String, Object> deploymentSettings = new HashMap<String, Object>();
    deploymentSettings.put(DeploymentSettings.IS_BPMN20_XSD_VALIDATION_ENABLED, deploymentBuilder.isBpmn20XsdValidationEnabled());
    deploymentSettings.put(DeploymentSettings.IS_PROCESS_VALIDATION_ENABLED, deploymentBuilder.isProcessValidationEnabled());
    
    // 实际部署
    commandContext
      .getProcessEngineConfiguration()
      .getDeploymentManager()
      .deploy(deployment, deploymentSettings);
    
    if (deploymentBuilder.getProcessDefinitionsActivationDate() != null) {
      scheduleProcessDefinitionActivation(commandContext, deployment);
    }
    
    if (commandContext.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
	    //......
        //省略触发ENTITY_INITIALIZED事件
    }
    
    return deployment;
  }

上面代码会进行部署去重、插入缓存、部署设定、实际部署、触发ENTITY_INITIALIZED等操作。我们关注28-32行实际部署,调用DeploymentManager的deploy方法。查看DeploymentManager.java:

public void deploy(DeploymentEntity deployment) {
    deploy(deployment, null);
}

public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) {
    for (Deployer deployer: deployers) {
        deployer.deploy(deployment, deploymentSettings);
    }
}

上面是使用了流程引擎初始化时配置的部署器。默认情况下部署器是BpmnDeployer,因此我们看看BpmnDeployer.java:

public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) {
    log.debug("Processing deployment {}", deployment.getName());
    
    List<ProcessDefinitionEntity> processDefinitions = new ArrayList<ProcessDefinitionEntity>();
    Map<String, ResourceEntity> resources = deployment.getResources();
    Map<String, BpmnModel> bpmnModelMap = new HashMap<String, BpmnModel>();

    final ProcessEngineConfigurationImpl processEngineConfiguration = Context.getProcessEngineConfiguration();
    for (String resourceName : resources.keySet()) {

      log.info("Processing resource {}", resourceName);
      if (isBpmnResource(resourceName)) {
        ResourceEntity resource = resources.get(resourceName);
        byte[] bytes = resource.getBytes();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
        
        BpmnParse bpmnParse = bpmnParser
          .createParse()
          .sourceInputStream(inputStream)
          .setSourceSystemId(resourceName)
          .deployment(deployment)
          .name(resourceName);
        
        if (deploymentSettings != null) {
        	//......
        	//省略相关校验设置项
        }
        
        bpmnParse.execute();
        
        for (ProcessDefinitionEntity processDefinition: bpmnParse.getProcessDefinitions()) {
          processDefinition.setResourceName(resourceName);
          
          if (deployment.getTenantId() != null) {
          	processDefinition.setTenantId(deployment.getTenantId()); // process definition inherits the tenant id
          }
          
          String diagramResourceName = getDiagramResourceForProcess(resourceName, processDefinition.getKey(), resources);
                   

          if(deployment.isNew()) {
            //......
            //省略资源文件生成
          }
          
          processDefinition.setDiagramResourceName(diagramResourceName);
          processDefinitions.add(processDefinition);
          bpmnModelMap.put(processDefinition.getKey(), bpmnParse.getBpmnModel());
        }
      }

上面代码很长,解析xml文档的29行调用在 bpmnParse.execute()。直到这里,就是解析流程文档的入口。

 

流程文档解析过程

上图是从流程文档解析到bpmnModel过程的简化过程。其中只列出了Process元素相关的解析,其实还有调用document、association/subProcess等元素的解析器,与Process元素类似。总体思路就是先新建一个BpmnModel,然后解析流程文档的外围元素和通用元素并添加到BpmnModel,再之后解析事件、活动、网关等元素添加到BpmnModel,最后返回BpmnModel。

 

跟踪流程文档解析代码

继续跟踪BpmnParse.java:

  public BpmnParse execute() {
    try {

    	ProcessEngineConfigurationImpl processEngineConfiguration = Context.getProcessEngineConfiguration();
      BpmnXMLConverter converter = new BpmnXMLConverter();
      
      boolean enableSafeBpmnXml = false;
      String encoding = null;
      if (processEngineConfiguration != null) {
        enableSafeBpmnXml = processEngineConfiguration.isEnableSafeBpmnXml();
        encoding = processEngineConfiguration.getXmlEncoding();
      }
      
      if (encoding != null) {
        bpmnModel = converter.convertToBpmnModel(streamSource, validateSchema, enableSafeBpmnXml, encoding);
      } else {
        bpmnModel = converter.convertToBpmnModel(streamSource, validateSchema, enableSafeBpmnXml);
      }
      
      // XSD validation goes first, then process/semantic validation
      if (validateProcess) {
        ......
        //省略校验bpmnModel有效性
      }
      
      // Validation successfull (or no validation)
      createImports();
      createItemDefinitions();
      createMessages();
      createOperations();
      transformProcessDefinitions();
      
    } catch (Exception e) {
      if (e instanceof ActivitiException) {
        throw (ActivitiException) e;
      } else if (e instanceof XMLException) {
        throw (XMLException) e;
      } else {
        throw new ActivitiException("Error parsing XML", e);
      }
    }

    return this;
  }

上面代码中14-18行converter.convertToBpmnModel就是把bpmn文件转化成bpmnModel。除此之外31行transformProcessDefinitions()也是一个比较重要的调用,会调用对象解析器,把bpmnModel中的元素解析成对应的对象,让流程虚拟机识别,这部分以后再细述。接下来看BpmnXMLConverter.java,由于代码量较大,为了避免篇幅过长,只展示最主要的代码:

public BpmnModel convertToBpmnModel(InputStreamProvider inputStreamProvider, boolean validateSchema, boolean enableSafeBpmnXml) {
    return convertToBpmnModel(inputStreamProvider, validateSchema, enableSafeBpmnXml, DEFAULT_ENCODING);
}
  
public BpmnModel convertToBpmnModel(InputStreamProvider inputStreamProvider, boolean validateSchema, boolean enableSafeBpmnXml, String encoding) {
    XMLInputFactory xif = XMLInputFactory.newInstance();
    //......
    //省略
    InputStreamReader in = null;
    try {
        in = new InputStreamReader(inputStreamProvider.getInputStream(), encoding);
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        //......
        //省略
      return convertToBpmnModel(xtr);
    } catch (UnsupportedEncodingException e) {
      //......
    } catch (XMLStreamException e) {
      //......
    } finally {
      //......
    }
}

public BpmnModel convertToBpmnModel(XMLStreamReader xtr) { 
    BpmnModel model = new BpmnModel();
    model.setStartEventFormTypes(startEventFormTypes);
    model.setUserTaskFormTypes(userTaskFormTypes);
    try {
        Process activeProcess = null;
        List<SubProcess> activeSubProcessList = new ArrayList<SubProcess>();
        while (xtr.hasNext()) {
            //......根据标签名调用对应的parser解析
            if (ELEMENT_DEFINITIONS.equals(xtr.getLocalName())) {
                definitionsParser.parse(xtr, model);//解析根元素
            }
            //......
            else if (ELEMENT_PROCESS.equals(xtr.getLocalName())) {
                Process process = processParser.parse(xtr, model);//解析process元素
                if (process != null) {
                    activeProcess = process;	
                }
            }
            else {//解析事件、网关、活动等元素
                if (!activeSubProcessList.isEmpty() && ELEMENT_MULTIINSTANCE.equalsIgnoreCase(xtr.getLocalName())) {
                    multiInstanceParser.parseChildElement(xtr, activeSubProcessList.get(activeSubProcessList.size() - 1), model);
                } else if (convertersToBpmnMap.containsKey(xtr.getLocalName())) {
                    if (activeProcess != null) {
                        BaseBpmnXMLConverter converter = convertersToBpmnMap.get(xtr.getLocalName());
                        converter.convertToBpmnModel(xtr, model, activeProcess, activeSubProcessList);
                    }
                }
            }
        }

        for (Process process : model.getProcesses()) {//处理连线、边界事件、子流程等
            for (Pool pool : model.getPools()) {
                if (process.getId().equals(pool.getProcessRef())) {
                    pool.setExecutable(process.isExecutable());
                }
            }
            processFlowElements(process.getFlowElements(), process);
        }
    } catch (XMLException e) {
        throw e;
    } catch (Exception e) {
        LOGGER.error("Error processing BPMN document", e);
        throw new XMLException("Error processing BPMN document", e);
    }
    return model;
}

上面32-54行对bpmn中各标签的解析,其中对process元素内部各元素例如userTask、squenceFlow、startEvent的解析是50行converter.convertToBpmnModel(xtr, model, activeProcess, activeSubProcessList)。这里调用BaseBpmnXMLConverter.java:

  public void convertToBpmnModel(XMLStreamReader xtr, BpmnModel model, Process activeProcess, 
      List<SubProcess> activeSubProcessList) throws Exception {
    
    String elementId = xtr.getAttributeValue(null, ATTRIBUTE_ID);
    String elementName = xtr.getAttributeValue(null, ATTRIBUTE_NAME);
    boolean async = parseAsync(xtr);
    boolean notExclusive = parseNotExclusive(xtr);
    String defaultFlow = xtr.getAttributeValue(null, ATTRIBUTE_DEFAULT);
    boolean isForCompensation = parseForCompensation(xtr);
    
    BaseElement parsedElement = convertXMLToElement(xtr, model);
    
    //......对解析的元素进行属性设置
  }

上面第11行BaseElement parsedElement = convertXMLToElement(xtr, model)会根据不同的XMLConverter调用对应的convertXMLToElement,即每个标签对应不同的XMLConverter进行解析。这些XMLConverter在BpmnXMLConverter.java的静态代码块中进行了配置:

static {
    // events
    addConverter(new EndEventXMLConverter());
    addConverter(new StartEventXMLConverter());
    
    // tasks
    addConverter(new BusinessRuleTaskXMLConverter());
    addConverter(new ManualTaskXMLConverter());
    addConverter(new ReceiveTaskXMLConverter());
    addConverter(new ScriptTaskXMLConverter());
    addConverter(new ServiceTaskXMLConverter());
    addConverter(new SendTaskXMLConverter());
    addConverter(new UserTaskXMLConverter());
    addConverter(new TaskXMLConverter());
    addConverter(new CallActivityXMLConverter());
    
    //......省略
}

如果对现有的XMLConverter不满意,还可以自行获取BpmnXMLConverter调用addConverter,由于其内部采用map管理,所以会替换原有的XMLConverter。从ProcessEngineConfigurationImpl.java初始化部署器的源码看来,除非用户使用addBpmnModel进行部署,否则必须在activiti.cfg.xml中配置自定义的BpmnParser替换默认BpmnParser。因为在部署期间默认的BpmnParser内部新建BpmnXMLConverter并完成一系列解析操作,用户无法替换自定义XMLConverter。

 

小结

通过本次代码跟踪,可以了解到activiti在流程部署的时候,对bpmn文件进行解析,转化后添加到bpmnModel中。后面的章节中,还会介绍activiti把bpmnModel中的元素转化成对应的实体,供流程虚拟机识别和运作。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值