文章目錄
解析準備
轉換器和解析器共同構建了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流,有兩種方式,
- 構建
org.activiti.bpmn.converter.util.InputStreamReader
- 直接構建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;
}
- 首先創建Process用於承載解析的對象,如果該元素沒有定義id則忽略
- 然後元素的id、name等熟悉
- 解析擴展屬性(會在下方詳細介紹)
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;
}
解析用戶任務的簡要邏輯是:
- 首先判斷是否存在formKey屬性,如果存在則使用則將其解析爲AlfrescoUserTask對象,不存在則是UserTask對象
- 解析位置信息
- 然後分別解析截止時間,分類,表單key,所屬人員,優先級,候選人、候選組、跳過表達式
- 解析擴展屬性
- 解析子元素
解析連線(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;
}
- 首先實例化SequenceFlow對象,在解析過程中會將相應屬性填充進該對象
- 解析位置信息
- 解析屬性,包括sourceRef、targetRef、skipExpression,當然還有常用屬性name等
- 解析子元素