工作流系列(5.3)-Activiti流程文件解析-解析擴展

解析擴展元素

之前我們看的都是Activiti對自有元素的解析,比如UserTask、Process、StartEvent等,而在Activiti中,我們也可以對自定義元素進行解析。對自定義元素的解析是通過org.activiti.bpmn.converter.util.BpmnXMLUtil.parseExtensionElement進行解析的。解析代碼如下

public static ExtensionElement parseExtensionElement(XMLStreamReader xtr) throws Exception {
    ExtensionElement extensionElement = new ExtensionElement();
    extensionElement.setName(xtr.getLocalName());
    if (StringUtils.isNotEmpty(xtr.getNamespaceURI())) {
      extensionElement.setNamespace(xtr.getNamespaceURI());
    }
    if (StringUtils.isNotEmpty(xtr.getPrefix())) {
      extensionElement.setNamespacePrefix(xtr.getPrefix());
    }

    for (int i = 0; i < xtr.getAttributeCount(); i++) {
      ExtensionAttribute extensionAttribute = new ExtensionAttribute();
      extensionAttribute.setName(xtr.getAttributeLocalName(i));
      extensionAttribute.setValue(xtr.getAttributeValue(i));
      if (StringUtils.isNotEmpty(xtr.getAttributeNamespace(i))) {
        extensionAttribute.setNamespace(xtr.getAttributeNamespace(i));
      }
      if (StringUtils.isNotEmpty(xtr.getAttributePrefix(i))) {
        extensionAttribute.setNamespacePrefix(xtr.getAttributePrefix(i));
      }
      extensionElement.addAttribute(extensionAttribute);
    }

    boolean readyWithExtensionElement = false;
    while (readyWithExtensionElement == false && xtr.hasNext()) {
      xtr.next();
      if (xtr.isCharacters() || XMLStreamReader.CDATA == xtr.getEventType()) {
        if (StringUtils.isNotEmpty(xtr.getText().trim())) {
          extensionElement.setElementText(xtr.getText().trim());
        }
      } else if (xtr.isStartElement()) {
        ExtensionElement childExtensionElement = parseExtensionElement(xtr);
        extensionElement.addChildElement(childExtensionElement);
      } else if (xtr.isEndElement() && extensionElement.getName().equalsIgnoreCase(xtr.getLocalName())) {
        readyWithExtensionElement = true;
      }
    }
    return extensionElement;
  }
  1. 首先實例化擴展元素,以便獲取定義的信息
  2. 設置名稱、命名空間、命名空間前綴、定義的屬性(名稱、命名空間、命名空間前綴)
  3. 判斷是否結束,
    1. 沒有結束則再依次判斷
    2. 有文本節點,進行賦值
    3. 有子節點,則獲取子節點屬性
    4. 是對應的結束節點,跳出循環

比如說如下配置

<?xml version="1.0" encoding="UTF-8"?>
<definitions
        xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
        xmlns:activiti="http://activiti.org/bpmn"
        targetNamespace="Examples">
      <process id="test-worlfow" name="流程測試"  activiti:key="${bussneKey}" activiti:processHandler="defaultTaskService">
		<extensionElements>
			<activiti:page url="ccccc"></activiti:page>
            <activiti:bizConfig>
                <item key="bizType" value="authorize"/>
            </activiti:bizConfig>
		</extensionElements>      
        <startEvent id="theStart" />
        <sequenceFlow id="flow1" sourceRef="theStart" targetRef="userTask1" />
        <userTask id="userTask1" name="任務1" >
        <extensionElements>  
         	<activiti:toolbar >
         		<activiti:button type="general" name="submit" displayName="提交" targetRef="agree" enable="true">
         			<activiti:user policy="role" para="000100" enable="${true}"></activiti:user>
         	 </activiti:button>
         	</activiti:toolbar>
         	<activiti:bizConfig>
                <item key="stepType" value="待提交"/>
                <item key="other" value="xxx"/>
            </activiti:bizConfig>
         	<activiti:page url="/law/${1}.json?"></activiti:page>
         </extensionElements>
        </userTask>
        <sequenceFlow id="flow2" sourceRef="userTask1" targetRef="theEnd" />
        <endEvent id="theEnd" />
    </process>

</definitions>

以下是測試解析擴展元素的方法

public class TestParseExtensionElement {

    public static void main(String[] args) {
        String resource = "test-workflow.bpmn20.xml";
        InputStream xmlStream = TestParseExtensionElement.class.getClassLoader()
                .getResourceAsStream(resource);
        InputStreamSource xmlSource = new InputStreamSource(xmlStream);
        BpmnXMLConverter converter = new BpmnXMLConverter();
        BpmnModel model = converter.convertToBpmnModel(xmlSource, true, true);
        Process process = model.getMainProcess();
        Map<String, List<ExtensionElement>> extensionElements = process.getExtensionElements();
        displayElementAndChild(extensionElements);
    }

    private static void displayElementAndChild(
            Map<String, List<ExtensionElement>> extensionElements) {
        for (Entry<String, List<ExtensionElement>> entry : extensionElements.entrySet()) {
            List<ExtensionElement> values = entry.getValue();
            for (ExtensionElement e : values) {
                System.out.println("key : " + e.getName());
                displayAttribute(e);
                Map<String, List<ExtensionElement>> child = e.getChildElements();
                if (child == null || child.isEmpty()) {
                    continue;
                }
                displayElementAndChild(child);
            }
        }
    }

    private static void displayAttribute(ExtensionElement extensionElement) {
        Map<String, List<ExtensionAttribute>> attributes = extensionElement.getAttributes();
        Collection<List<ExtensionAttribute>> attrValues = attributes.values();
        if (attrValues == null || attrValues.isEmpty()) {
            return;
        }
        System.out.println(extensionElement.getName() + " attributes:");
        System.out.print("\t");
        for (List<ExtensionAttribute> list : attrValues) {
            for (ExtensionAttribute attr : list) {
                System.out.print(attr.getName() + ":" + attr.getValue() + ", ");
            }
        }
        System.out.println();
    }

}
  1. 首先我們讀取上文中提到的流程配置文件
  2. 調用BpmnXMLConverter中的converToBpmnModel方法獲取BpmnModel對象
  3. displayElementAndChild方法打印輸出元素以及子元素的信息
  4. displayAttribute方法輸出自定義屬性信息

解析自定義屬性

在之前的的幾個轉換類中,我們發現大部分都有要解析自定義屬性,可以簡單看一下對自定義屬性的解析,在org.activiti.bpmn.converter.util.BpmnXMLUtil類中,

 /**
   * 將所有的擴展屬性添加到元素中,並忽略blackLists中的擴展屬性
   * 
   * @param xtr
   * @param element
   * @param blackList 黑名單
   */
  public static void addCustomAttributes(XMLStreamReader xtr, BaseElement element, List<ExtensionAttribute>... blackLists) {
    for (int i = 0; i < xtr.getAttributeCount(); i++) {
      ExtensionAttribute extensionAttribute = new ExtensionAttribute();
      extensionAttribute.setName(xtr.getAttributeLocalName(i));
      extensionAttribute.setValue(xtr.getAttributeValue(i));
      if (StringUtils.isNotEmpty(xtr.getAttributeNamespace(i))) {
        extensionAttribute.setNamespace(xtr.getAttributeNamespace(i));
      }
      if (StringUtils.isNotEmpty(xtr.getAttributePrefix(i))) {
        extensionAttribute.setNamespacePrefix(xtr.getAttributePrefix(i));
      }
      if (!isBlacklisted(extensionAttribute, blackLists)) {
        element.addAttribute(extensionAttribute);
      }
    }
  }
  1. 解析擴展屬性比較簡單,基本邏輯就是將name、value、namespace等屬性解析
  2. 忽略黑名單中的屬性

黑名單屬性

  1. 全局屬性
    | 屬性| 含義|
    | – | --|
    | id |id,全局唯一 |
    | name|任務節點的名稱 |

  2. activiti屬性
    | 屬性| 命名空間 | 含義|
    | – | --| --|
    | aysn|http://activiti.org/bpmn|是否異步執行|
    | exclusive|http://activiti.org/bpmn|是否排他|
    | default||默認屬性|
    | isForCompensation|http://activiti.org/bpmn|是否補償|

  3. 元素特有屬性(userTask的屬性,process特有的屬性)

UserTask特有屬性,其命名空間都是http://activiti.org/bpmn

屬性 含義
formKey 任務節點關聯的form表單
dueDate 任務截止日期
businessCalendarName 業務日曆名稱
assignee 任務執行人
owner 任務所有者
priority 優先級
candidateUsers 候選執行人
candidateGroups 候選執行人分鐘
category 分類
extensionId 擴展id
skipExpression 跳過任務表達式

Process屬性,除了公共屬性,之外還有一下屬性,其命名空間都是http://activiti.org/bpmn

屬性 含義
isExecutable 是否可執行單
candidateStarterUsers 候選流程啓動執行人
candidateStarterGroups 候選流程啓動分組

解析子元素

各個元素的轉換器首先會調用抽象類BaseBpmnXMLConverter中的parseChildElements方法

 protected void parseChildElements(String elementName, BaseElement parentElement, BpmnModel model, XMLStreamReader xtr) throws Exception {
    parseChildElements(elementName, parentElement, null, model, xtr);
  }

  protected void parseChildElements(String elementName, BaseElement parentElement, Map<String, BaseChildElementParser> additionalParsers, BpmnModel model, XMLStreamReader xtr) throws Exception {

    Map<String, BaseChildElementParser> childParsers = new HashMap<String, BaseChildElementParser>();
    if (additionalParsers != null) {
      childParsers.putAll(additionalParsers);
    }
    BpmnXMLUtil.parseChildElements(elementName, parentElement, xtr, childParsers, model);
  }

然後再調用org.activiti.bpmn.converter.util.BpmnXMLUtil.parseChildElements方法,

 public static void parseChildElements(String elementName, BaseElement parentElement, XMLStreamReader xtr, 
      Map<String, BaseChildElementParser> childParsers, BpmnModel model) throws Exception {
    
    Map<String, BaseChildElementParser> localParserMap =
        new HashMap<String, BaseChildElementParser>(genericChildParserMap);
    if (childParsers != null) {
      localParserMap.putAll(childParsers);
    }

    boolean inExtensionElements = false;
    boolean readyWithChildElements = false;
    while (readyWithChildElements == false && xtr.hasNext()) {
      xtr.next();
      if (xtr.isStartElement()) {
        if (ELEMENT_EXTENSIONS.equals(xtr.getLocalName())) {
          inExtensionElements = true;
        } else if (localParserMap.containsKey(xtr.getLocalName())) {
          BaseChildElementParser childParser = localParserMap.get(xtr.getLocalName());
          //if we're into an extension element but the current element is not accepted by this parentElement then is read as a custom extension element
          if (inExtensionElements && !childParser.accepts(parentElement)) {
            ExtensionElement extensionElement = BpmnXMLUtil.parseExtensionElement(xtr);
            parentElement.addExtensionElement(extensionElement);
            continue;
          }
          localParserMap.get(xtr.getLocalName()).parseChildElement(xtr, parentElement, model);
        } else if (inExtensionElements) {
          ExtensionElement extensionElement = BpmnXMLUtil.parseExtensionElement(xtr);
          parentElement.addExtensionElement(extensionElement);
        }

      } else if (xtr.isEndElement()) {
        if (ELEMENT_EXTENSIONS.equals(xtr.getLocalName())) {
          inExtensionElements = false;
        } else if (elementName.equalsIgnoreCase(xtr.getLocalName())) {
          readyWithChildElements = true;
        }
      }
    }
  }

通過以上源碼我們可以看到解析子元素的步驟

  1. 首先初始化子元素解析器集合,包括通用的子元素的解析器以及每個元素單獨適用的的子元素解析器
  2. 解析擴展元素
    a) 如果判斷元素屬於子元素解析器集合中的類,則直接調用子元素解析
    b) 如果元素是擴展元素(extensionElement),則調用BpmnXMLUtil.parseExtensionElement(xtr)進行解析
  3. 有一個額外判斷,如果該元素是擴展元素,並且父類不能接受這個擴展元素,則它被解析爲一個擴展元素

以解析userTask元素爲例,
首先在UserTaskXMLConverter類中,加入瞭如下子元素解析器

	HumanPerformerParser humanPerformerParser = new HumanPerformerParser();
    childParserMap.put(humanPerformerParser.getElementName(), humanPerformerParser);
    PotentialOwnerParser potentialOwnerParser = new PotentialOwnerParser();
    childParserMap.put(potentialOwnerParser.getElementName(), potentialOwnerParser);
    CustomIdentityLinkParser customIdentityLinkParser = new CustomIdentityLinkParser();
    childParserMap.put(customIdentityLinkParser.getElementName(), customIdentityLinkParser);

以下是org.activiti.bpmn.converter.UserTaskXMLConverter.convertXMLToElement的部分代碼

protected BaseElement convertXMLToElement(XMLStreamReader xtr, BpmnModel model) throws Exception {
    ....
    parseChildElements(getXMLElementName(), userTask, childParserMap, model, xtr);
    return userTask;
  }

可以看出最終會將特有子元素解析器傳入並調用父類的方法實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章