之前我们看的都是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;
}
比如说如下配置
<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();
}
}
在之前的的几个转换类中,我们发现大部分都有要解析自定义属性,可以简单看一下对自定义属性的解析,在org.activiti.bpmn.converter.util.BpmnXMLUtil
类中,
/**
* 将所有的扩展属性添加到元素中,并忽略blackLists中的扩展属性
*
* @param xtr
* @param element
* @param blackList 黑名单
*/
public static void addCustomAttributes(XMLStreamReader xtr, BaseElement element, List... 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);
}
}
}
黑名单属性
全局属性
| 属性| 含义|
| – | --|
| id |id,全局唯一 |
| name|任务节点的名称 |
activiti属性
| 属性| 命名空间 | 含义|
| – | --| --|
| aysn|http://activiti.org/bpmn
|是否异步执行|
| exclusive|http://activiti.org/bpmn
|是否排他|
| default||默认属性|
| isForCompensation|http://activiti.org/bpmn
|是否补偿|
元素特有属性(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 additionalParsers, BpmnModel model, XMLStreamReader xtr) throws Exception {
Map childParsers = new HashMap();
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;
}
}
}
}
通过以上源码我们可以看到解析子元素的步骤
BpmnXMLUtil.parseExtensionElement(xtr)
进行解析以解析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;
}
可以看出最终会将特有子元素解析器传入并调用父类的方法实现。