在使用開源 Camunda 流程引擎框架做二次開發,有時候會在固定節點添加固定的參數及參數值,流程在流轉到此節點時,我們如何運用Camunda從中取出配制的參數呢?
下面首先介紹 Camunda 支持配制哪些參數:
-
String
-
Map
-
List
-
Script
-
String or Expression
在Camunda中,除開1類型之外,其它四種都是以對象的方式保存在流程中(解析出來保存在 ACT_GE_BYTEARRAY 表中)。配制String類型的是在 Extensions ;其它4種是在 Input/Output
上面介紹了使用 Camunda Modeler 流程圖工具配制參數,下面來看下流程圖是如何取到這些值的。
讀取這些值的方法還是很簡單,只有一句代碼:
Map<String, Object> map = runtimeService.getVariables(task.getExecutionId());
讀取配制的值很簡單,那有沒有想過這些值是怎么取到的呢?
大膽猜想一下,就是在部署流程解析XML文件的時候,這些值就解析出來了,然后在保存在緩存中。
有了猜想,下面再來驗證我們的猜想。
protected DeploymentWithDefinitions doExecute(final CommandContext commandContext) { DeploymentManager deploymentManager = commandContext.getDeploymentManager(); // load deployment handler ProcessEngine processEngine = commandContext.getProcessEngineConfiguration().getProcessEngine(); deploymentHandler = commandContext.getProcessEngineConfiguration() .getDeploymentHandlerFactory() .buildDeploymentHandler(processEngine); Set<String> deploymentIds = getAllDeploymentIds(deploymentBuilder); if (!deploymentIds.isEmpty()) { String[] deploymentIdArray = deploymentIds.toArray(new String[deploymentIds.size()]); List<DeploymentEntity> deployments = deploymentManager.findDeploymentsByIds(deploymentIdArray); ensureDeploymentsWithIdsExists(deploymentIds, deployments); } checkCreateAndReadDeployments(commandContext, deploymentIds); // set deployment name if it should retrieved from an existing deployment String nameFromDeployment = deploymentBuilder.getNameFromDeployment(); setDeploymentName(nameFromDeployment, deploymentBuilder, commandContext); // get resources to re-deploy List<ResourceEntity> resources = getResources(deploymentBuilder, commandContext); // .. and add them the builder addResources(resources, deploymentBuilder); Collection<String> resourceNames = deploymentBuilder.getResourceNames(); if (resourceNames == null || resourceNames.isEmpty()) { throw new NotValidException("No deployment resources contained to deploy."); } // perform deployment DeploymentWithDefinitions deployment = commandContext.runWithoutAuthorization(() -> { acquireExclusiveLock(commandContext); DeploymentEntity deploymentToRegister = initDeployment(); Map<String, ResourceEntity> resourcesToDeploy = resolveResourcesToDeploy(commandContext, deploymentToRegister); Map<String, ResourceEntity> resourcesToIgnore = new HashMap<>(deploymentToRegister.getResources()); resourcesToIgnore.keySet().removeAll(resourcesToDeploy.keySet()); // save initial deployment resources before they are replaced with only the deployed ones CandidateDeployment candidateDeployment = CandidateDeploymentImpl.fromDeploymentEntity(deploymentToRegister); if (!resourcesToDeploy.isEmpty()) { LOG.debugCreatingNewDeployment(); deploymentToRegister.setResources(resourcesToDeploy); deploy(commandContext, deploymentToRegister); } else { // if there are no resources to be deployed, find an existing deployment String duplicateDeploymentId = deploymentHandler.determineDuplicateDeployment(candidateDeployment); deploymentToRegister = commandContext.getDeploymentManager().findDeploymentById(duplicateDeploymentId); } scheduleProcessDefinitionActivation(commandContext, deploymentToRegister); if(deploymentBuilder instanceof ProcessApplicationDeploymentBuilder) { // for process application deployments, job executor registration // is managed by the ProcessApplicationManager ProcessApplicationRegistration registration = registerProcessApplication( commandContext, deploymentToRegister, candidateDeployment, resourcesToIgnore.values()); return new ProcessApplicationDeploymentImpl(deploymentToRegister, registration); } else { registerWithJobExecutor(commandContext, deploymentToRegister); } return deploymentToRegister; }); createUserOperationLog(deploymentBuilder, deployment, commandContext); return deployment; } 、********************、 public <T> T runWithoutAuthorization(Callable<T> runnable) { CommandContext commandContext = Context.getCommandContext(); boolean authorizationEnabled = commandContext.isAuthorizationCheckEnabled(); try { commandContext.disableAuthorizationCheck(); return runnable.call(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new ProcessEngineException(e); } finally { if (authorizationEnabled) { commandContext.enableAuthorizationCheck(); } } }
代碼調試發現,上面的代碼就是部署流程的核心代碼。
代碼一步一步跟蹤調試,終是沒有發現解析參數的地方?上面的猜想失敗了
那Camunda 到底是如何處理參數的呢?調試了好幾天,都沒有發現解析參數的地方!真的都被搞的很點失去信心了。
就在3月17日下午,我重新整理了調整了思路。
是不是直接從表里面查詢出來的?
試試跟蹤 runtimeService.getVariables(task.getExecutionId()) 這個方法,終於發現了一點眉目,真是從表查詢出來的。
public VariableMap execute(CommandContext commandContext) { ensureNotNull("executionId", executionId); ExecutionEntity execution = commandContext .getExecutionManager() .findExecutionById(executionId); ensureNotNull("execution " + executionId + " doesn't exist", "execution", execution); checkGetExecutionVariables(execution, commandContext); VariableMapImpl executionVariables = new VariableMapImpl(); // collect variables from execution 核心方法 execution.collectVariables(executionVariables, variableNames, isLocal, deserializeValues); return executionVariables; }
// selectVariablesByExecutionId 對應執行的SQL語句 SELECT RES.*, ( case when RES.TASK_ID_ is not null and RES.EXECUTION_ID_ is not null then EXECUTION.ACT_INST_ID_ when RES.CASE_EXECUTION_ID_ is not null then RES.CASE_EXECUTION_ID_ when EXECUTION.PARENT_ID_ is null and RES.IS_CONCURRENT_LOCAL_ = 0 then EXECUTION.ID_ when EXECUTION.IS_SCOPE_ = 1 and EXECUTION.PARENT_ID_ is not null and RES.IS_CONCURRENT_LOCAL_ = 0 then PARENT_EXECUTION.ACT_INST_ID_ else EXECUTION.ACT_INST_ID_ end ) ACT_INST_ID_ FROM ACT_RU_VARIABLE RES LEFT JOIN ACT_RU_EXECUTION EXECUTION ON RES.EXECUTION_ID_ = EXECUTION.ID_ LEFT JOIN ACT_RU_EXECUTION PARENT_EXECUTION ON EXECUTION.PARENT_ID_ = PARENT_EXECUTION.ID_ WHERE EXECUTION_ID_ = '54854e59-a5c7-11ec-a5f8-92b1d713a145' AND TASK_ID_ is null
執行上面的SQL后,會返回
List<
VariableInstanceEntity
> 列表對象,在 VariableInstanceEntity 對象中有
ByteArrayField 屬性,它保證的是 一條 ACT_GE_BYTEARRAY(保存的對象信息,也就是需要序列化的參數) 表的數據信息。
總結 runtimeService.getVariables 執行過程:
1、根據當前任務ID獲取當 executionId,根據 executionId 查詢 ACT_RU_VARIABLE 參數表
2、判斷 ACT_RU_EXECUTION 表中的 PARENT_ID_ 是否有值,有值就繼續根據此值查詢 ACT_RU_VARIABLE
3、 根據查詢的 ACT_RU_VARIABLE 結果中 BYTEARRAY_ID_ 有值,就根據此ID去查詢ACT_GE_BYTEARRAY 資源信息表,反序列化對象