工作流系列(5.2)-Activiti流程文件解析源碼分析

解析準備

轉換器和解析器共同構建了Activiti的轉換基礎。

準備轉換器

從配置文件的到BpmnModel的轉換都是通過org.activiti.bpmn.converterBpmnXMLConvert類來進行,在解析之前會準備各種轉換器,具體的代碼是

static {
    // events
    addConverter(new EndEventXMLConverter());
    addConverter(new StartEventXMLConverter());

    // tasks
   ....
  }

只列出了部分,沒有全部列出。從之前的文章中,我們可以看到對於每個元素都有相對應的轉化器(解析器)與其對應,而每個元素公共的屬性也有相應的解析器去解析。

解析器

對於各個子元素或公共元素的解析,大部分是通過org.activiti.bpmn.converter.util.BpmnXMLUtil類來進行解析的,當然其也會首先加載不同子元素的解析器。

 static {
    addGenericParser(new ActivitiEventListenerParser());
    ...
    addGenericParser(new IOSpecificationParser());
    addGenericParser(new MessageEventDefinitionParser());
    addGenericParser(new MultiInstanceParser());
    addGenericParser(new SignalEventDefinitionParser());
    addGenericParser(new TaskListenerParser());
    ....
  }

Activiti自有解析

解析入口

在BpmnXMLConvert中,我們使用convertToBpmnModel方法將流程配置文件解析爲BpmnModel模型

 public BpmnModel convertToBpmnModel(InputStreamProvider, boolean, boolean)
 public BpmnModel convertToBpmnModel(InputStreamProvider, boolean, boolean, String)
 public BpmnModel convertToBpmnModel(XMLStreamReader)

在解析配置文件過程中,我們需要XML流,有兩種方式,

  1. 構建org.activiti.bpmn.converter.util.InputStreamReader
  2. 直接構建XMLStreamReader
    當然最終的處理都是會基於XMLStreamReader

執行解析

我們閱讀源碼會發現,所有的轉換最終都是調用public BpmnModel convertToBpmnModel(XMLStreamReader)方法,我們可以大致瀏覽一下其方法

public BpmnModel convertToBpmnModel(XMLStreamReader){
    BpmnModel model = new BpmnModel();
    model.setStartEventFormTypes(startEventFormTypes);
    model.setUserTaskFormTypes(userTaskFormTypes);
    try {
	  //定義主流程
      Process activeProcess = null;
      //準備子流程列表
      List<SubProcess> activeSubProcessList = new ArrayList<SubProcess>();
      while (xtr.hasNext()) {
        try {
          xtr.next();
        } catch (Exception e) {
          LOGGER.debug("Error reading XML document", e);
          throw new XMLException("Error reading XML", e);
        }
		//子流程或事務子流程或特別子流程
        if (xtr.isEndElement() && (ELEMENT_SUBPROCESS.equals(xtr.getLocalName()) || 
            ELEMENT_TRANSACTION.equals(xtr.getLocalName()) ||
            ELEMENT_ADHOC_SUBPROCESS.equals(xtr.getLocalName()))) {
          activeSubProcessList.remove(activeSubProcessList.size() - 1);
        }
		//不是開始元素跳過執行
        if (xtr.isStartElement() == false) {
          continue;
        }
        //解析定義信息
        if (ELEMENT_DEFINITIONS.equals(xtr.getLocalName())) {
			 definitionsParser.parse(xtr, model);	  
        } else if (ELEMENT_RESOURCE.equals(xtr.getLocalName())) {
          //解析資源信息
          resourceParser.parse(xtr, model);
         } else if (ELEMENT_SIGNAL.equals(xtr.getLocalName())) {
			signalParser.parse(xtr, model);
		 } else if (ELEMENT_MESSAGE.equals(xtr.getLocalName())) {
            messageParser.parse(xtr, model);
        } else if (ELEMENT_ERROR.equals(xtr.getLocalName())) {
          if (StringUtils.isNotEmpty(xtr.getAttributeValue(null, ATTRIBUTE_ID))) {
            model.addError(xtr.getAttributeValue(null, ATTRIBUTE_ID), xtr.getAttributeValue(null, ATTRIBUTE_ERROR_CODE));
          }
        }
        .....//省略大部分if判斷
        } 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;
 } 

通過簡單瀏覽其源碼,我們發現是通過對XML文件流進行一系列的判斷,根據遇到的元素不同,而調用不同的轉化器(解析器),最終將整個BpmnModel構建出來。

探祕轉換器(解析器)

因爲有很多轉化器或解析器,我們不能一一解析,我們將挑選幾個比較典型的元素轉換器進行分析。對於子元素、擴展元素的解析,放到下一節中。

ProcessParser(流程元素解析)

在convertToBpmnModel方法中,如果遇到Process元素,就會通過調用org.activiti.bpmn.converter.parser.ProcessParser中的parse方法來進行解析

public BpmnModel convertToBpmnModel(XMLStreamReader){
...
   else if (ELEMENT_PROCESS.equals(xtr.getLocalName())) {
      Process process = processParser.parse(xtr, model);
      if (process != null) {
          activeProcess = process;
       }
	}
...
 return model;
}

以下是org.activiti.bpmn.converter.parser.ProcessParser.parse()方法的源碼

public Process parse(XMLStreamReader xtr, BpmnModel model) throws Exception {
    //創建Process對象,用於表示該元素
    Process process = null;
    if (StringUtils.isNotEmpty(xtr.getAttributeValue(null, ATTRIBUTE_ID))) {
      //解析processId
      String processId = xtr.getAttributeValue(null, ATTRIBUTE_ID);
      process = new Process();
      process.setId(processId);
      //解析元素在XML文件中的的位置信息
      BpmnXMLUtil.addXMLLocation(process, xtr);
      process.setName(xtr.getAttributeValue(null, ATTRIBUTE_NAME));
      //獲取可執行屬性
      if (StringUtils.isNotEmpty(xtr.getAttributeValue(null, ATTRIBUTE_PROCESS_EXECUTABLE))) {
        process.setExecutable(Boolean.parseBoolean(xtr.getAttributeValue(null, ATTRIBUTE_PROCESS_EXECUTABLE)));
      }
      //獲取流程開始時用戶 
      String candidateUsersString = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_PROCESS_CANDIDATE_USERS);
      if (StringUtils.isNotEmpty(candidateUsersString)) {
        List<String> candidateUsers = BpmnXMLUtil.parseDelimitedList(candidateUsersString);
        process.setCandidateStarterUsers(candidateUsers);
      }
      //獲取流程開始時分組    
      String candidateGroupsString = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_PROCESS_CANDIDATE_GROUPS);
      if (StringUtils.isNotEmpty(candidateGroupsString)) {
        List<String> candidateGroups = BpmnXMLUtil.parseDelimitedList(candidateGroupsString);
        process.setCandidateStarterGroups(candidateGroups);
      }
	  //解析擴展元素
      BpmnXMLUtil.addCustomAttributes(xtr, process, ProcessExport.defaultProcessAttributes);
      model.getProcesses().add(process);

    }
    return process;
  }
  1. 首先創建Process用於承載解析的對象,如果該元素沒有定義id則忽略
  2. 然後元素的id、name等熟悉
  3. 解析擴展屬性(會在下方詳細介紹)

UserTaskXMLConverter(用戶任務轉換器)

解析元素userTask時,使用org.activiti.bpmn.converter.UserTaskXMLConverter進行解析。

調用轉換器

在BpmnXMLConverter中對其的調用是通過獲取轉換器Map中對應元素的轉化進行解析的。

BaseBpmnXMLConverter converter = convertersToBpmnMap.get(xtr.getLocalName());
converter.convertToBpmnModel(xtr, model, activeProcess, activeSubProcessList);
轉換方法
  @Override
  @SuppressWarnings("unchecked")
  protected BaseElement convertXMLToElement(XMLStreamReader xtr, BpmnModel model) throws Exception {
    String formKey = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_FORM_FORMKEY);
    UserTask userTask = null;
    //判斷是是否存在formKey(表單key)
    if (StringUtils.isNotEmpty(formKey)) {
      if (model.getUserTaskFormTypes() != null && model.getUserTaskFormTypes().contains(formKey)) {
        userTask = new AlfrescoUserTask();
      }
    }
    if (userTask == null) {
      userTask = new UserTask();
    }
    //解析位置信息
    BpmnXMLUtil.addXMLLocation(userTask, xtr);
    //然後分別解析截止時間,分類,表單key,所屬人員,優先級,候選人、候選組、跳過表達式
    userTask.setDueDate(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_DUEDATE));
    userTask.setBusinessCalendarName(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_BUSINESS_CALENDAR_NAME));
    userTask.setCategory(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CATEGORY));
    userTask.setFormKey(formKey);
    userTask.setAssignee(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_ASSIGNEE)); 
    userTask.setOwner(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_OWNER));
    userTask.setPriority(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_PRIORITY));
    
    if (StringUtils.isNotEmpty(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CANDIDATEUSERS))) {
      String expression = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CANDIDATEUSERS);
      userTask.getCandidateUsers().addAll(parseDelimitedList(expression));
    } 
    
    if (StringUtils.isNotEmpty(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CANDIDATEGROUPS))) {
      String expression = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_CANDIDATEGROUPS);
      userTask.getCandidateGroups().addAll(parseDelimitedList(expression));
    }
    
    userTask.setExtensionId(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_SERVICE_EXTENSIONID));

    if (StringUtils.isNotEmpty(xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_SKIP_EXPRESSION))) {
      String expression = xtr.getAttributeValue(ACTIVITI_EXTENSIONS_NAMESPACE, ATTRIBUTE_TASK_USER_SKIP_EXPRESSION);
      userTask.setSkipExpression(expression);
    }
    //解析自定義屬性
    BpmnXMLUtil.addCustomAttributes(xtr, userTask, defaultElementAttributes, 
        defaultActivityAttributes, defaultUserTaskAttributes);
    //解析子元素
    parseChildElements(getXMLElementName(), userTask, childParserMap, model, xtr);
    
    return userTask;
  }

解析用戶任務的簡要邏輯是:

  1. 首先判斷是否存在formKey屬性,如果存在則使用則將其解析爲AlfrescoUserTask對象,不存在則是UserTask對象
  2. 解析位置信息
  3. 然後分別解析截止時間,分類,表單key,所屬人員,優先級,候選人、候選組、跳過表達式
  4. 解析擴展屬性
  5. 解析子元素

解析連線(sequenceFlow)

另外一個比較重要的元素是連線,它的解析類是org.activiti.bpmn.converter.SequenceFlowXMLConverter

protected BaseElement convertXMLToElement(XMLStreamReader xtr, BpmnModel model) throws Exception {
    SequenceFlow sequenceFlow = new SequenceFlow();
    BpmnXMLUtil.addXMLLocation(sequenceFlow, xtr);
    sequenceFlow.setSourceRef(xtr.getAttributeValue(null, ATTRIBUTE_FLOW_SOURCE_REF));
    sequenceFlow.setTargetRef(xtr.getAttributeValue(null, ATTRIBUTE_FLOW_TARGET_REF));
    sequenceFlow.setName(xtr.getAttributeValue(null, ATTRIBUTE_NAME));
    sequenceFlow.setSkipExpression(xtr.getAttributeValue(null, ATTRIBUTE_FLOW_SKIP_EXPRESSION));

    parseChildElements(getXMLElementName(), sequenceFlow, model, xtr);

    return sequenceFlow;
  }
  1. 首先實例化SequenceFlow對象,在解析過程中會將相應屬性填充進該對象
  2. 解析位置信息
  3. 解析屬性,包括sourceRef、targetRef、skipExpression,當然還有常用屬性name等
  4. 解析子元素
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章