前言
本章开始,会对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中的元素转化成对应的实体,供流程虚拟机识别和运作。