From 6b49dd42d32cf5100b80a2b23a59b9f7420961db Mon Sep 17 00:00:00 2001 From: Tijs Rademakers Date: Wed, 21 Oct 2015 16:43:35 +0200 Subject: [PATCH] Add dynamic bpmn to Activiti 5 compatibility engine --- .../Activiti5CompatibilityHandler.java | 6 + .../impl/cmd/GetProcessDefinitionInfoCmd.java | 10 +- .../cmd/SaveProcessDefinitionInfoCmd.java | 8 + .../DefaultActiviti5CompatibilityHandler.java | 21 + .../DefaultProcessEngineFactory.java | 1 + .../activiti5/engine/DynamicBpmnService.java | 91 ++++ .../org/activiti5/engine/ProcessEngine.java | 2 + .../engine/ProcessEngineConfiguration.java | 11 + .../engine/impl/DynamicBpmnServiceImpl.java | 309 +++++++++++++ .../engine/impl/ProcessEngineImpl.java | 8 + ...askDelegateExpressionActivityBehavior.java | 21 +- ...ServiceTaskExpressionActivityBehavior.java | 20 +- .../behavior/UserTaskActivityBehavior.java | 183 ++++++-- .../impl/bpmn/deployer/BpmnDeployer.java | 54 ++- .../impl/bpmn/helper/ClassDelegate.java | 20 +- .../DefaultActivityBehaviorFactory.java | 9 +- .../cfg/ProcessEngineConfigurationImpl.java | 32 ++ .../impl/cmd/GetProcessDefinitionInfoCmd.java | 56 +++ .../cmd/SaveProcessDefinitionInfoCmd.java | 74 ++++ .../engine/impl/context/Context.java | 61 ++- .../impl/history/DefaultHistoryManager.java | 14 +- .../impl/interceptor/CommandContext.java | 5 + .../CommandContextInterceptor.java | 1 + .../impl/persistence/AbstractManager.java | 5 + .../persistence/deploy/DeploymentManager.java | 9 + .../deploy/ProcessDefinitionInfoCache.java | 121 +++++ .../ProcessDefinitionInfoCacheObject.java | 49 ++ .../entity/DeploymentEntityManager.java | 3 +- .../entity/ProcessDefinitionInfoEntity.java | 80 ++++ .../ProcessDefinitionInfoEntityManager.java | 97 ++++ .../engine/impl/task/TaskDefinition.java | 8 + .../mapping/entity/ProcessDefinitionInfo.xml | 74 ++++ .../org/activiti5/db/mapping/mappings.xml | 1 + .../impl/test/AbstractActivitiTestCase.java | 3 + .../bpmn/servicetask/DummyServiceTask2.java | 31 ++ .../test/bpmn/servicetask/DummyTestBean.java | 27 ++ .../servicetask/DummyTestDelegateBean.java | 28 ++ .../servicetask/DynamicServiceTaskTest.java | 159 +++++++ .../DisabledDefinitionInfoCacheTest.java | 115 +++++ .../bpmn/usertask/DynamicUserTaskTest.java | 417 ++++++++++++++++++ .../src/test/resources/activiti.cfg.xml | 2 + ...iceTaskTest.testChangeClassName.bpmn20.xml | 28 ++ ...st.testChangeDelegateExpression.bpmn20.xml | 24 + ...ceTaskTest.testChangeExpression.bpmn20.xml | 24 + ...foCacheTest.testChangeClassName.bpmn20.xml | 28 ++ ...InfoCacheTest.testChangeFormKey.bpmn20.xml | 20 + .../DynamicUserTaskTest.assignment.bpmn20.xml | 20 + .../DynamicUserTaskTest.basictask.bpmn20.xml | 20 + ...cUserTaskTest.testChangeFormKey.bpmn20.xml | 20 + ...testChangeFormKeyWithExpression.bpmn20.xml | 20 + .../test/bpmn/usertask/activiti.cfg.xml | 22 + 51 files changed, 2404 insertions(+), 68 deletions(-) create mode 100644 modules/activiti5-engine/src/main/java/org/activiti5/engine/DynamicBpmnService.java create mode 100644 modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/DynamicBpmnServiceImpl.java create mode 100644 modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cmd/GetProcessDefinitionInfoCmd.java create mode 100644 modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cmd/SaveProcessDefinitionInfoCmd.java create mode 100644 modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/ProcessDefinitionInfoCache.java create mode 100644 modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/ProcessDefinitionInfoCacheObject.java create mode 100644 modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/ProcessDefinitionInfoEntity.java create mode 100644 modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/ProcessDefinitionInfoEntityManager.java create mode 100644 modules/activiti5-engine/src/main/resources/org/activiti5/db/mapping/entity/ProcessDefinitionInfo.xml create mode 100644 modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyServiceTask2.java create mode 100644 modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyTestBean.java create mode 100644 modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyTestDelegateBean.java create mode 100644 modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.java create mode 100644 modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.java create mode 100644 modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.java create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeClassName.bpmn20.xml create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeDelegateExpression.bpmn20.xml create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeExpression.bpmn20.xml create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.testChangeClassName.bpmn20.xml create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.testChangeFormKey.bpmn20.xml create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.assignment.bpmn20.xml create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.basictask.bpmn20.xml create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.testChangeFormKey.bpmn20.xml create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.testChangeFormKeyWithExpression.bpmn20.xml create mode 100644 modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/activiti.cfg.xml diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/compatibility/Activiti5CompatibilityHandler.java b/modules/activiti-engine/src/main/java/org/activiti/engine/compatibility/Activiti5CompatibilityHandler.java index 9c64089dfd..4bad50ef9a 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/compatibility/Activiti5CompatibilityHandler.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/compatibility/Activiti5CompatibilityHandler.java @@ -34,6 +34,8 @@ import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Attachment; import org.activiti.engine.task.Comment; +import com.fasterxml.jackson.databind.node.ObjectNode; + /** * @author Joram Barrez * @author Tijs Rademakers @@ -50,6 +52,10 @@ public interface Activiti5CompatibilityHandler { BpmnModel getProcessDefinitionBpmnModel(String processDefinitionId); + ObjectNode getProcessDefinitionInfo(String processDefinitionId); + + void saveProcessDefinitionInfo(String processDefinitionId, ObjectNode infoNode); + void addCandidateStarter(String processDefinitionId, String userId, String groupId); void deleteCandidateStarter(String processDefinitionId, String userId, String groupId); diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/GetProcessDefinitionInfoCmd.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/GetProcessDefinitionInfoCmd.java index 92f89a87f6..c966871f2c 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/GetProcessDefinitionInfoCmd.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/GetProcessDefinitionInfoCmd.java @@ -15,10 +15,13 @@ package org.activiti.engine.impl.cmd; import java.io.Serializable; import org.activiti.engine.ActivitiIllegalArgumentException; +import org.activiti.engine.compatibility.Activiti5CompatibilityHandler; import org.activiti.engine.impl.interceptor.Command; import org.activiti.engine.impl.interceptor.CommandContext; import org.activiti.engine.impl.persistence.deploy.DeploymentManager; import org.activiti.engine.impl.persistence.deploy.ProcessDefinitionInfoCacheObject; +import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.activiti.engine.impl.util.Activiti5Util; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -44,7 +47,12 @@ public class GetProcessDefinitionInfoCmd implements Command, Seriali ObjectNode resultNode = null; DeploymentManager deploymentManager = commandContext.getProcessEngineConfiguration().getDeploymentManager(); // make sure the process definition is in the cache - deploymentManager.findDeployedProcessDefinitionById(processDefinitionId); + ProcessDefinitionEntity processDefinition = deploymentManager.findDeployedProcessDefinitionById(processDefinitionId); + if (Activiti5Util.isActiviti5ProcessDefinition(commandContext, processDefinition)) { + Activiti5CompatibilityHandler activiti5CompatibilityHandler = Activiti5Util.getActiviti5CompatibilityHandler(); + return activiti5CompatibilityHandler.getProcessDefinitionInfo(processDefinitionId); + } + ProcessDefinitionInfoCacheObject definitionInfoCacheObject = deploymentManager.getProcessDefinitionInfoCache().get(processDefinitionId); if (definitionInfoCacheObject != null) { resultNode = definitionInfoCacheObject.getInfoNode(); diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/SaveProcessDefinitionInfoCmd.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/SaveProcessDefinitionInfoCmd.java index cbe7b6bcdd..61cf628c4b 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/SaveProcessDefinitionInfoCmd.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/SaveProcessDefinitionInfoCmd.java @@ -16,10 +16,12 @@ import java.io.Serializable; import org.activiti.engine.ActivitiException; import org.activiti.engine.ActivitiIllegalArgumentException; +import org.activiti.engine.compatibility.Activiti5CompatibilityHandler; import org.activiti.engine.impl.interceptor.Command; import org.activiti.engine.impl.interceptor.CommandContext; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionInfoEntity; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionInfoEntityManager; +import org.activiti.engine.impl.util.Activiti5Util; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -49,6 +51,12 @@ public class SaveProcessDefinitionInfoCmd implements Command, Serializable throw new ActivitiIllegalArgumentException("process definition info node is null"); } + if (Activiti5Util.isActiviti5ProcessDefinitionId(commandContext, processDefinitionId)) { + Activiti5CompatibilityHandler activiti5CompatibilityHandler = Activiti5Util.getActiviti5CompatibilityHandler(); + activiti5CompatibilityHandler.saveProcessDefinitionInfo(processDefinitionId, infoNode); + return null; + } + ProcessDefinitionInfoEntityManager definitionInfoEntityManager = commandContext.getProcessDefinitionInfoEntityManager(); ProcessDefinitionInfoEntity definitionInfoEntity = definitionInfoEntityManager.findProcessDefinitionInfoByProcessDefinitionId(processDefinitionId); if (definitionInfoEntity == null) { diff --git a/modules/activiti5-compatibility/src/main/java/org/activiti/compatibility/DefaultActiviti5CompatibilityHandler.java b/modules/activiti5-compatibility/src/main/java/org/activiti/compatibility/DefaultActiviti5CompatibilityHandler.java index b7bf61d4d4..55e9b043d4 100644 --- a/modules/activiti5-compatibility/src/main/java/org/activiti/compatibility/DefaultActiviti5CompatibilityHandler.java +++ b/modules/activiti5-compatibility/src/main/java/org/activiti/compatibility/DefaultActiviti5CompatibilityHandler.java @@ -74,6 +74,8 @@ import org.activiti5.engine.impl.pvm.delegate.ActivityExecution; import org.activiti5.engine.impl.scripting.ScriptingEngines; import org.activiti5.engine.repository.DeploymentBuilder; +import com.fasterxml.jackson.databind.node.ObjectNode; + /** * @author Joram Barrez * @author Tijs Rademakers @@ -156,6 +158,25 @@ public class DefaultActiviti5CompatibilityHandler implements Activiti5Compatibil } } + public ObjectNode getProcessDefinitionInfo(String processDefinitionId) { + try { + return getProcessEngine().getDynamicBpmnService().getProcessDefinitionInfo(processDefinitionId); + + } catch (org.activiti5.engine.ActivitiException e) { + handleActivitiException(e); + return null; + } + } + + public void saveProcessDefinitionInfo(String processDefinitionId, ObjectNode infoNode) { + try { + getProcessEngine().getDynamicBpmnService().saveProcessDefinitionInfo(processDefinitionId, infoNode); + + } catch (org.activiti5.engine.ActivitiException e) { + handleActivitiException(e); + } + } + public void deleteCandidateStarter(String processDefinitionId, String userId, String groupId) { try { if (userId != null) { diff --git a/modules/activiti5-compatibility/src/main/java/org/activiti/compatibility/DefaultProcessEngineFactory.java b/modules/activiti5-compatibility/src/main/java/org/activiti/compatibility/DefaultProcessEngineFactory.java index 0643054ab3..3958f53865 100644 --- a/modules/activiti5-compatibility/src/main/java/org/activiti/compatibility/DefaultProcessEngineFactory.java +++ b/modules/activiti5-compatibility/src/main/java/org/activiti/compatibility/DefaultProcessEngineFactory.java @@ -102,6 +102,7 @@ public class DefaultProcessEngineFactory { activiti5Configuration.setCreateDiagramOnDeploy(activiti6Configuration.isCreateDiagramOnDeploy()); activiti5Configuration.setProcessDefinitionCacheLimit(activiti6Configuration.getProcessDefinitionCacheLimit()); + activiti5Configuration.setEnableProcessDefinitionInfoCache(activiti6Configuration.isEnableProcessDefinitionInfoCache()); if (activiti6Configuration.isAsyncExecutorEnabled()) { activiti5Configuration.setAsyncExecutorEnabled(true); diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/DynamicBpmnService.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/DynamicBpmnService.java new file mode 100644 index 0000000000..69fcf07a85 --- /dev/null +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/DynamicBpmnService.java @@ -0,0 +1,91 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** Service providing access to the repository of process definitions and deployments. + * + * @author Tijs Rademakers + */ +public interface DynamicBpmnService { + + ObjectNode getProcessDefinitionInfo(String processDefinitionId); + + void saveProcessDefinitionInfo(String processDefinitionId, ObjectNode infoNode); + + ObjectNode changeServiceTaskClassName(String id, String className); + + void changeServiceTaskClassName(String id, String className, ObjectNode infoNode); + + ObjectNode changeServiceTaskExpression(String id, String expression); + + void changeServiceTaskExpression(String id, String expression, ObjectNode infoNode); + + ObjectNode changeServiceTaskDelegateExpression(String id, String expression); + + void changeServiceTaskDelegateExpression(String id, String expression, ObjectNode infoNode); + + ObjectNode changeUserTaskName(String id, String name); + + void changeUserTaskName(String id, String name, ObjectNode infoNode); + + ObjectNode changeUserTaskDescription(String id, String description); + + void changeUserTaskDescription(String id, String description, ObjectNode infoNode); + + ObjectNode changeUserTaskDueDate(String id, String dueDate); + + void changeUserTaskDueDate(String id, String dueDate, ObjectNode infoNode); + + ObjectNode changeUserTaskPriority(String id, String priority); + + void changeUserTaskPriority(String id, String priority, ObjectNode infoNode); + + ObjectNode changeUserTaskCategory(String id, String category); + + void changeUserTaskCategory(String id, String category, ObjectNode infoNode); + + ObjectNode changeUserTaskFormKey(String id, String formKey); + + void changeUserTaskFormKey(String id, String formKey, ObjectNode infoNode); + + ObjectNode changeUserTaskAssignee(String id, String assignee); + + void changeUserTaskAssignee(String id, String assignee, ObjectNode infoNode); + + ObjectNode changeUserTaskOwner(String id, String owner); + + void changeUserTaskOwner(String id, String owner, ObjectNode infoNode); + + ObjectNode changeUserTaskCandidateUser(String id, String candidateUser, boolean overwriteOtherChangedEntries); + + void changeUserTaskCandidateUser(String id, String candidateUser, boolean overwriteOtherChangedEntries, ObjectNode infoNode); + + ObjectNode changeUserTaskCandidateGroup(String id, String candidateGroup, boolean overwriteOtherChangedEntries); + + void changeUserTaskCandidateGroup(String id, String candidateGroup, boolean overwriteOtherChangedEntries, ObjectNode infoNode); + + ObjectNode getBpmnElementProperties(String id, ObjectNode infoNode); + + ObjectNode changeLocalizationName(String language, String id, String value); + + void changeLocalizationName(String language, String id, String value, ObjectNode infoNode); + + ObjectNode changeLocalizationDescription(String language, String id, String value); + + void changeLocalizationDescription(String language, String id, String value, ObjectNode infoNode); + + ObjectNode getLocalizationElementProperties(String language, String id, ObjectNode infoNode); +} \ No newline at end of file diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/ProcessEngine.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/ProcessEngine.java index d9e18c50de..cc51ceb4d0 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/ProcessEngine.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/ProcessEngine.java @@ -72,6 +72,8 @@ public interface ProcessEngine { IdentityService getIdentityService(); ManagementService getManagementService(); + + DynamicBpmnService getDynamicBpmnService(); ProcessEngineConfiguration getProcessEngineConfiguration(); } diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/ProcessEngineConfiguration.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/ProcessEngineConfiguration.java index f03642a29e..9e1e8d51e9 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/ProcessEngineConfiguration.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/ProcessEngineConfiguration.java @@ -213,6 +213,8 @@ public abstract class ProcessEngineConfiguration { */ protected boolean useClassForNameClassLoading = true; protected ProcessEngineLifecycleListener processEngineLifecycleListener; + + protected boolean enableProcessDefinitionInfoCache = false; /** use one of the static createXxxx methods instead */ protected ProcessEngineConfiguration() { @@ -809,4 +811,13 @@ public abstract class ProcessEngineConfiguration { this.asyncFailedJobWaitTime = asyncFailedJobWaitTime; return this; } + + public boolean isEnableProcessDefinitionInfoCache() { + return enableProcessDefinitionInfoCache; + } + + public ProcessEngineConfiguration setEnableProcessDefinitionInfoCache(boolean enableProcessDefinitionInfoCache) { + this.enableProcessDefinitionInfoCache = enableProcessDefinitionInfoCache; + return this; + } } diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/DynamicBpmnServiceImpl.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/DynamicBpmnServiceImpl.java new file mode 100644 index 0000000000..d56f70ca3a --- /dev/null +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/DynamicBpmnServiceImpl.java @@ -0,0 +1,309 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine.impl; + +import org.activiti.engine.DynamicBpmnConstants; +import org.activiti5.engine.DynamicBpmnService; +import org.activiti5.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.activiti5.engine.impl.cmd.GetProcessDefinitionInfoCmd; +import org.activiti5.engine.impl.cmd.SaveProcessDefinitionInfoCmd; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + + + +/** + * @author Tijs Rademakers + */ +public class DynamicBpmnServiceImpl extends ServiceImpl implements DynamicBpmnService, DynamicBpmnConstants { + + public DynamicBpmnServiceImpl(ProcessEngineConfigurationImpl processEngineConfiguration) { + super(processEngineConfiguration); + } + + public ObjectNode getProcessDefinitionInfo(String processDefinitionId) { + return commandExecutor.execute(new GetProcessDefinitionInfoCmd(processDefinitionId)); + } + + public void saveProcessDefinitionInfo(String processDefinitionId, ObjectNode infoNode) { + commandExecutor.execute(new SaveProcessDefinitionInfoCmd(processDefinitionId, infoNode)); + } + + public ObjectNode changeServiceTaskClassName(String id, String className) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeServiceTaskClassName(id, className, infoNode); + return infoNode; + } + + public void changeServiceTaskClassName(String id, String className, ObjectNode infoNode) { + setElementProperty(id, SERVICE_TASK_CLASS_NAME, className, infoNode); + } + + public ObjectNode changeServiceTaskExpression(String id, String expression) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeServiceTaskExpression(id, expression, infoNode); + return infoNode; + } + + public void changeServiceTaskExpression(String id, String expression, ObjectNode infoNode) { + setElementProperty(id, SERVICE_TASK_EXPRESSION, expression, infoNode); + } + + public ObjectNode changeServiceTaskDelegateExpression(String id, String expression) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeServiceTaskDelegateExpression(id, expression, infoNode); + return infoNode; + } + + public void changeServiceTaskDelegateExpression(String id, String expression, ObjectNode infoNode) { + setElementProperty(id, SERVICE_TASK_DELEGATE_EXPRESSION, expression, infoNode); + } + + public ObjectNode changeUserTaskName(String id, String name) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskName(id, name, infoNode); + return infoNode; + } + + public void changeUserTaskName(String id, String name, ObjectNode infoNode) { + setElementProperty(id, USER_TASK_NAME, name, infoNode); + } + + public ObjectNode changeUserTaskDescription(String id, String description) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskDescription(id, description, infoNode); + return infoNode; + } + + public void changeUserTaskDescription(String id, String description, ObjectNode infoNode) { + setElementProperty(id, USER_TASK_DESCRIPTION, description, infoNode); + } + + public ObjectNode changeUserTaskDueDate(String id, String dueDate) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskDueDate(id, dueDate, infoNode); + return infoNode; + } + + public void changeUserTaskDueDate(String id, String dueDate, ObjectNode infoNode) { + setElementProperty(id, USER_TASK_DUEDATE, dueDate, infoNode); + } + + public ObjectNode changeUserTaskPriority(String id, String priority) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskPriority(id, priority, infoNode); + return infoNode; + } + + public void changeUserTaskPriority(String id, String priority, ObjectNode infoNode) { + setElementProperty(id, USER_TASK_PRIORITY, priority, infoNode); + } + + public ObjectNode changeUserTaskCategory(String id, String category) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskCategory(id, category, infoNode); + return infoNode; + } + + public void changeUserTaskCategory(String id, String category, ObjectNode infoNode) { + setElementProperty(id, USER_TASK_CATEGORY, category, infoNode); + } + + public ObjectNode changeUserTaskFormKey(String id, String formKey) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskFormKey(id, formKey, infoNode); + return infoNode; + } + + public void changeUserTaskFormKey(String id, String formKey, ObjectNode infoNode) { + setElementProperty(id, USER_TASK_FORM_KEY, formKey, infoNode); + } + + public ObjectNode changeUserTaskAssignee(String id, String assignee) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskAssignee(id, assignee, infoNode); + return infoNode; + } + + public void changeUserTaskAssignee(String id, String assignee, ObjectNode infoNode) { + setElementProperty(id, USER_TASK_ASSIGNEE, assignee, infoNode); + } + + public ObjectNode changeUserTaskOwner(String id, String owner) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskOwner(id, owner, infoNode); + return infoNode; + } + + public void changeUserTaskOwner(String id, String owner, ObjectNode infoNode) { + setElementProperty(id, USER_TASK_OWNER, owner, infoNode); + } + + public ObjectNode changeUserTaskCandidateUser(String id, String candidateUser, boolean overwriteOtherChangedEntries) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskCandidateUser(id, candidateUser, overwriteOtherChangedEntries, infoNode); + return infoNode; + } + + public void changeUserTaskCandidateUser(String id, String candidateUser, boolean overwriteOtherChangedEntries, ObjectNode infoNode) { + ArrayNode valuesNode = null; + if (overwriteOtherChangedEntries) { + valuesNode = processEngineConfiguration.getObjectMapper().createArrayNode(); + } else { + if (doesElementPropertyExist(id, USER_TASK_CANDIDATE_USERS, infoNode)) { + valuesNode = (ArrayNode) infoNode.get(BPMN_NODE).get(id).get(USER_TASK_CANDIDATE_USERS); + } + + if (valuesNode == null || valuesNode.isNull()) { + valuesNode = processEngineConfiguration.getObjectMapper().createArrayNode(); + } + } + + valuesNode.add(candidateUser); + setElementProperty(id, USER_TASK_CANDIDATE_USERS, valuesNode, infoNode); + } + + public ObjectNode changeUserTaskCandidateGroup(String id, String candidateGroup, boolean overwriteOtherChangedEntries) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeUserTaskCandidateGroup(id, candidateGroup, overwriteOtherChangedEntries, infoNode); + return infoNode; + } + + public void changeUserTaskCandidateGroup(String id, String candidateGroup, boolean overwriteOtherChangedEntries, ObjectNode infoNode) { + ArrayNode valuesNode = null; + if (overwriteOtherChangedEntries) { + valuesNode = processEngineConfiguration.getObjectMapper().createArrayNode(); + } else { + if (doesElementPropertyExist(id, USER_TASK_CANDIDATE_GROUPS, infoNode)) { + valuesNode = (ArrayNode) infoNode.get(BPMN_NODE).get(id).get(USER_TASK_CANDIDATE_GROUPS); + } + + if (valuesNode == null || valuesNode.isNull()) { + valuesNode = processEngineConfiguration.getObjectMapper().createArrayNode(); + } + } + + valuesNode.add(candidateGroup); + setElementProperty(id, USER_TASK_CANDIDATE_GROUPS, valuesNode, infoNode); + } + + public ObjectNode getBpmnElementProperties(String id, ObjectNode infoNode) { + ObjectNode propertiesNode = null; + ObjectNode bpmnNode = getBpmnNode(infoNode); + if (bpmnNode != null) { + propertiesNode = (ObjectNode) bpmnNode.get(id); + } + return propertiesNode; + } + + public ObjectNode changeLocalizationName(String language, String id, String value) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeLocalizationName(language, id, value, infoNode); + return infoNode; + } + + public void changeLocalizationName(String language, String id, String value, ObjectNode infoNode) { + setLocalizationProperty(language, id, LOCALIZATION_NAME, value, infoNode); + } + + public ObjectNode changeLocalizationDescription(String language, String id, String value) { + ObjectNode infoNode = processEngineConfiguration.getObjectMapper().createObjectNode(); + changeLocalizationDescription(language, id, value, infoNode); + return infoNode; + } + + public void changeLocalizationDescription(String language, String id, String value, ObjectNode infoNode) { + setLocalizationProperty(language, id, LOCALIZATION_DESCRIPTION, value, infoNode); + } + + public ObjectNode getLocalizationElementProperties(String language, String id, ObjectNode infoNode) { + ObjectNode propertiesNode = null; + ObjectNode localizationNode = getLocalizationNode(infoNode); + if (localizationNode != null) { + JsonNode languageNode = localizationNode.get(language); + if (languageNode != null) { + propertiesNode = (ObjectNode) languageNode.get(id); + } + } + return propertiesNode; + } + + protected boolean doesElementPropertyExist(String id, String propertyName, ObjectNode infoNode) { + boolean exists = false; + if (infoNode.get(BPMN_NODE) != null && infoNode.get(BPMN_NODE).get(id) != null && infoNode.get(BPMN_NODE).get(id).get(propertyName) != null) { + JsonNode propNode = infoNode.get(BPMN_NODE).get(id).get(propertyName); + if (propNode.isNull() == false) { + exists = true; + } + } + return exists; + } + + protected void setElementProperty(String id, String propertyName, String propertyValue, ObjectNode infoNode) { + ObjectNode bpmnNode = createOrGetBpmnNode(infoNode); + if (bpmnNode.has(id) == false) { + bpmnNode.put(id, processEngineConfiguration.getObjectMapper().createObjectNode()); + } + + ((ObjectNode) bpmnNode.get(id)).put(propertyName, propertyValue); + } + + protected void setElementProperty(String id, String propertyName, JsonNode propertyValue, ObjectNode infoNode) { + ObjectNode bpmnNode = createOrGetBpmnNode(infoNode); + if (bpmnNode.has(id) == false) { + bpmnNode.put(id, processEngineConfiguration.getObjectMapper().createObjectNode()); + } + + ((ObjectNode) bpmnNode.get(id)).put(propertyName, propertyValue); + } + + protected ObjectNode createOrGetBpmnNode(ObjectNode infoNode) { + if (infoNode.has(BPMN_NODE) == false) { + infoNode.put(BPMN_NODE, processEngineConfiguration.getObjectMapper().createObjectNode()); + } + return (ObjectNode) infoNode.get(BPMN_NODE); + } + + protected ObjectNode getBpmnNode(ObjectNode infoNode) { + return (ObjectNode) infoNode.get(BPMN_NODE); + } + + protected void setLocalizationProperty(String language, String id, String propertyName, String propertyValue, ObjectNode infoNode) { + ObjectNode localizationNode = createOrGetLocalizationNode(infoNode); + if (localizationNode.has(language) == false) { + localizationNode.put(language, processEngineConfiguration.getObjectMapper().createObjectNode()); + } + + ObjectNode languageNode = (ObjectNode) localizationNode.get(language); + if (languageNode.has(id) == false) { + languageNode.put(id, processEngineConfiguration.getObjectMapper().createObjectNode()); + } + + ((ObjectNode) languageNode.get(id)).put(propertyName, propertyValue); + } + + protected ObjectNode createOrGetLocalizationNode(ObjectNode infoNode) { + if (infoNode.has(LOCALIZATION_NODE) == false) { + infoNode.put(LOCALIZATION_NODE, processEngineConfiguration.getObjectMapper().createObjectNode()); + } + return (ObjectNode) infoNode.get(LOCALIZATION_NODE); + } + + protected ObjectNode getLocalizationNode(ObjectNode infoNode) { + return (ObjectNode) infoNode.get(LOCALIZATION_NODE); + } + +} \ No newline at end of file diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/ProcessEngineImpl.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/ProcessEngineImpl.java index baf187bbaa..a30426e055 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/ProcessEngineImpl.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/ProcessEngineImpl.java @@ -14,6 +14,7 @@ package org.activiti5.engine.impl; import java.util.Map; +import org.activiti5.engine.DynamicBpmnService; import org.activiti5.engine.FormService; import org.activiti5.engine.HistoryService; import org.activiti5.engine.IdentityService; @@ -37,6 +38,7 @@ import org.slf4j.LoggerFactory; /** * @author Tom Baeyens + * @author Tijs Rademakers */ public class ProcessEngineImpl implements ProcessEngine { @@ -50,6 +52,7 @@ public class ProcessEngineImpl implements ProcessEngine { protected TaskService taskService; protected FormService formService; protected ManagementService managementService; + protected DynamicBpmnService dynamicBpmnService; protected JobExecutor jobExecutor; protected AsyncExecutor asyncExecutor; protected CommandExecutor commandExecutor; @@ -68,6 +71,7 @@ public class ProcessEngineImpl implements ProcessEngine { this.taskService = processEngineConfiguration.getTaskService(); this.formService = processEngineConfiguration.getFormService(); this.managementService = processEngineConfiguration.getManagementService(); + this.dynamicBpmnService = processEngineConfiguration.getDynamicBpmnService(); this.jobExecutor = processEngineConfiguration.getJobExecutor(); this.asyncExecutor = processEngineConfiguration.getAsyncExecutor(); this.commandExecutor = processEngineConfiguration.getCommandExecutor(); @@ -153,6 +157,10 @@ public class ProcessEngineImpl implements ProcessEngine { public FormService getFormService() { return formService; } + + public DynamicBpmnService getDynamicBpmnService() { + return dynamicBpmnService; + } public ProcessEngineConfigurationImpl getProcessEngineConfiguration() { return processEngineConfiguration; diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java index a55a97ba8f..26b070ad13 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java @@ -15,6 +15,7 @@ package org.activiti5.engine.impl.bpmn.behavior; import java.util.List; import org.activiti.engine.ActivitiException; +import org.activiti.engine.DynamicBpmnConstants; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.Expression; import org.activiti.engine.delegate.JavaDelegate; @@ -30,6 +31,9 @@ import org.activiti5.engine.impl.delegate.ActivityBehaviorInvocation; import org.activiti5.engine.impl.delegate.JavaDelegateInvocation; import org.activiti5.engine.impl.pvm.delegate.ActivityExecution; import org.activiti5.engine.impl.pvm.delegate.SignallableActivityBehavior; +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.databind.node.ObjectNode; /** @@ -43,11 +47,13 @@ import org.activiti5.engine.impl.pvm.delegate.SignallableActivityBehavior; */ public class ServiceTaskDelegateExpressionActivityBehavior extends TaskActivityBehavior { + protected String serviceTaskId; protected Expression expression; protected Expression skipExpression; private final List fieldDeclarations; - public ServiceTaskDelegateExpressionActivityBehavior(Expression expression, Expression skipExpression, List fieldDeclarations) { + public ServiceTaskDelegateExpressionActivityBehavior(String serviceTaskId, Expression expression, Expression skipExpression, List fieldDeclarations) { + this.serviceTaskId = serviceTaskId; this.expression = expression; this.skipExpression = skipExpression; this.fieldDeclarations = fieldDeclarations; @@ -69,9 +75,18 @@ public class ServiceTaskDelegateExpressionActivityBehavior extends TaskActivityB if (!isSkipExpressionEnabled || (isSkipExpressionEnabled && !SkipExpressionUtil.shouldSkipFlowElement(activityExecution, skipExpression))) { + if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) { + ObjectNode taskElementProperties = Context.getBpmnOverrideElementProperties(serviceTaskId, execution.getProcessDefinitionId()); + if (taskElementProperties != null && taskElementProperties.has(DynamicBpmnConstants.SERVICE_TASK_DELEGATE_EXPRESSION)) { + String overrideExpression = taskElementProperties.get(DynamicBpmnConstants.SERVICE_TASK_DELEGATE_EXPRESSION).asText(); + if (StringUtils.isNotEmpty(overrideExpression) && overrideExpression.equals(expression.getExpressionText()) == false) { + expression = Context.getProcessEngineConfiguration().getExpressionManager().createExpression(overrideExpression); + } + } + } + // Note: we can't cache the result of the expression, because the - // execution can change: eg. - // delegateExpression='${mySpringBeanFactory.randomSpringBean()}' + // execution can change: eg. delegateExpression='${mySpringBeanFactory.randomSpringBean()}' Object delegate = expression.getValue(execution); ClassDelegate.applyFieldDeclaration(fieldDeclarations, delegate); diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/ServiceTaskExpressionActivityBehavior.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/ServiceTaskExpressionActivityBehavior.java index a31f058df3..f1d94c47bb 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/ServiceTaskExpressionActivityBehavior.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/ServiceTaskExpressionActivityBehavior.java @@ -14,12 +14,17 @@ package org.activiti5.engine.impl.bpmn.behavior; import org.activiti.engine.ActivitiException; +import org.activiti.engine.DynamicBpmnConstants; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.Expression; import org.activiti5.engine.delegate.BpmnError; import org.activiti5.engine.impl.bpmn.helper.ErrorPropagation; import org.activiti5.engine.impl.bpmn.helper.SkipExpressionUtil; +import org.activiti5.engine.impl.context.Context; import org.activiti5.engine.impl.pvm.delegate.ActivityExecution; +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.databind.node.ObjectNode; /** * ActivityBehavior that evaluates an expression when executed. Optionally, it @@ -33,11 +38,13 @@ import org.activiti5.engine.impl.pvm.delegate.ActivityExecution; */ public class ServiceTaskExpressionActivityBehavior extends TaskActivityBehavior { + protected String serviceTaskId; protected Expression expression; protected Expression skipExpression; protected String resultVariable; - public ServiceTaskExpressionActivityBehavior(Expression expression, Expression skipExpression, String resultVariable) { + public ServiceTaskExpressionActivityBehavior(String serviceTaskId, Expression expression, Expression skipExpression, String resultVariable) { + this.serviceTaskId = serviceTaskId; this.expression = expression; this.skipExpression = skipExpression; this.resultVariable = resultVariable; @@ -50,6 +57,17 @@ public class ServiceTaskExpressionActivityBehavior extends TaskActivityBehavior boolean isSkipExpressionEnabled = SkipExpressionUtil.isSkipExpressionEnabled(activityExecution, skipExpression); if (!isSkipExpressionEnabled || (isSkipExpressionEnabled && !SkipExpressionUtil.shouldSkipFlowElement(activityExecution, skipExpression))) { + + if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) { + ObjectNode taskElementProperties = Context.getBpmnOverrideElementProperties(serviceTaskId, execution.getProcessDefinitionId()); + if (taskElementProperties != null && taskElementProperties.has(DynamicBpmnConstants.SERVICE_TASK_EXPRESSION)) { + String overrideExpression = taskElementProperties.get(DynamicBpmnConstants.SERVICE_TASK_EXPRESSION).asText(); + if (StringUtils.isNotEmpty(overrideExpression) && overrideExpression.equals(expression.getExpressionText()) == false) { + expression = Context.getProcessEngineConfiguration().getExpressionManager().createExpression(overrideExpression); + } + } + } + value = expression.getValue(execution); if (resultVariable != null) { execution.setVariable(resultVariable, value); diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java index 89631f01c6..b9d3be5ad1 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java @@ -15,11 +15,13 @@ package org.activiti5.engine.impl.bpmn.behavior; import java.util.Arrays; import java.util.Collection; import java.util.Date; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import org.activiti.engine.DynamicBpmnConstants; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.Expression; import org.activiti.engine.delegate.TaskListener; @@ -31,6 +33,7 @@ import org.activiti5.engine.impl.bpmn.helper.SkipExpressionUtil; import org.activiti5.engine.impl.calendar.BusinessCalendar; import org.activiti5.engine.impl.calendar.DueDateBusinessCalendar; import org.activiti5.engine.impl.context.Context; +import org.activiti5.engine.impl.el.ExpressionManager; import org.activiti5.engine.impl.persistence.entity.ExecutionEntity; import org.activiti5.engine.impl.persistence.entity.TaskEntity; import org.activiti5.engine.impl.pvm.delegate.ActivityExecution; @@ -38,6 +41,9 @@ import org.activiti5.engine.impl.task.TaskDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + /** * activity implementation for the user task. * @@ -49,9 +55,11 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { private static final Logger LOGGER = LoggerFactory.getLogger(UserTaskActivityBehavior.class); + protected String userTaskId; protected TaskDefinition taskDefinition; - public UserTaskActivityBehavior(TaskDefinition taskDefinition) { + public UserTaskActivityBehavior(String userTaskId, TaskDefinition taskDefinition) { + this.userTaskId = userTaskId; this.taskDefinition = taskDefinition; } @@ -59,32 +67,84 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { ActivityExecution activityExecution = (ActivityExecution) execution; TaskEntity task = TaskEntity.createAndInsert(activityExecution); task.setExecution(execution); + + Expression activeNameExpression = null; + Expression activeDescriptionExpression = null; + Expression activeDueDateExpression = null; + Expression activePriorityExpression = null; + Expression activeCategoryExpression = null; + Expression activeFormKeyExpression = null; + Expression activeSkipExpression = null; + Expression activeAssigneeExpression = null; + Expression activeOwnerExpression = null; + Set activeCandidateUserExpressions = null; + Set activeCandidateGroupExpressions = null; + + if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) { + ObjectNode taskElementProperties = Context.getBpmnOverrideElementProperties(userTaskId, execution.getProcessDefinitionId()); + activeNameExpression = getActiveValue(taskDefinition.getNameExpression(), DynamicBpmnConstants.USER_TASK_NAME, taskElementProperties); + taskDefinition.setNameExpression(activeNameExpression); + activeDescriptionExpression = getActiveValue(taskDefinition.getDescriptionExpression(), DynamicBpmnConstants.USER_TASK_DESCRIPTION, taskElementProperties); + taskDefinition.setDescriptionExpression(activeDescriptionExpression); + activeDueDateExpression = getActiveValue(taskDefinition.getDueDateExpression(), DynamicBpmnConstants.USER_TASK_DUEDATE, taskElementProperties); + taskDefinition.setDueDateExpression(activeDueDateExpression); + activePriorityExpression = getActiveValue(taskDefinition.getPriorityExpression(), DynamicBpmnConstants.USER_TASK_PRIORITY, taskElementProperties); + taskDefinition.setPriorityExpression(activePriorityExpression); + activeCategoryExpression = getActiveValue(taskDefinition.getCategoryExpression(), DynamicBpmnConstants.USER_TASK_CATEGORY, taskElementProperties); + taskDefinition.setCategoryExpression(activeCategoryExpression); + activeFormKeyExpression = getActiveValue(taskDefinition.getFormKeyExpression(), DynamicBpmnConstants.USER_TASK_FORM_KEY, taskElementProperties); + taskDefinition.setFormKeyExpression(activeFormKeyExpression); + activeSkipExpression = getActiveValue(taskDefinition.getSkipExpression(), DynamicBpmnConstants.TASK_SKIP_EXPRESSION, taskElementProperties); + taskDefinition.setSkipExpression(activeSkipExpression); + activeAssigneeExpression = getActiveValue(taskDefinition.getAssigneeExpression(), DynamicBpmnConstants.USER_TASK_ASSIGNEE, taskElementProperties); + taskDefinition.setAssigneeExpression(activeAssigneeExpression); + activeOwnerExpression = getActiveValue(taskDefinition.getOwnerExpression(), DynamicBpmnConstants.USER_TASK_OWNER, taskElementProperties); + taskDefinition.setOwnerExpression(activeOwnerExpression); + activeCandidateUserExpressions = getActiveValueSet(taskDefinition.getCandidateUserIdExpressions(), DynamicBpmnConstants.USER_TASK_CANDIDATE_USERS, taskElementProperties); + taskDefinition.setCandidateUserIdExpressions(activeCandidateUserExpressions); + activeCandidateGroupExpressions = getActiveValueSet(taskDefinition.getCandidateGroupIdExpressions(), DynamicBpmnConstants.USER_TASK_CANDIDATE_GROUPS, taskElementProperties); + taskDefinition.setCandidateGroupIdExpressions(activeCandidateGroupExpressions); + + } else { + activeNameExpression = taskDefinition.getNameExpression(); + activeDescriptionExpression = taskDefinition.getDescriptionExpression(); + activeDueDateExpression = taskDefinition.getDueDateExpression(); + activePriorityExpression = taskDefinition.getPriorityExpression(); + activeCategoryExpression = taskDefinition.getCategoryExpression(); + activeFormKeyExpression = taskDefinition.getFormKeyExpression(); + activeSkipExpression = taskDefinition.getSkipExpression(); + activeAssigneeExpression = taskDefinition.getAssigneeExpression(); + activeOwnerExpression = taskDefinition.getOwnerExpression(); + activeCandidateUserExpressions = taskDefinition.getCandidateUserIdExpressions(); + activeCandidateGroupExpressions = taskDefinition.getCandidateGroupIdExpressions(); + } + task.setTaskDefinition(taskDefinition); - if (taskDefinition.getNameExpression() != null) { + if (activeNameExpression != null) { String name = null; try { - name = (String) taskDefinition.getNameExpression().getValue(execution); + name = (String) activeNameExpression.getValue(execution); } catch (ActivitiException e) { - name = taskDefinition.getNameExpression().getExpressionText(); + name = activeNameExpression.getExpressionText(); LOGGER.warn("property not found in task name expression " + e.getMessage()); } task.setName(name); } - if (taskDefinition.getDescriptionExpression() != null) { + if (activeDescriptionExpression != null) { String description = null; try { - description = (String) taskDefinition.getDescriptionExpression().getValue(execution); + description = (String) activeDescriptionExpression.getValue(execution); } catch (ActivitiException e) { - description = taskDefinition.getDescriptionExpression().getExpressionText(); + description = activeDescriptionExpression.getExpressionText(); LOGGER.warn("property not found in task description expression " + e.getMessage()); } task.setDescription(description); } - if (taskDefinition.getDueDateExpression() != null) { - Object dueDate = taskDefinition.getDueDateExpression().getValue(execution); + if (activeDueDateExpression != null) { + Object dueDate = activeDueDateExpression.getValue(execution); if (dueDate != null) { if (dueDate instanceof Date) { task.setDueDate((Date) dueDate); @@ -96,13 +156,13 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { task.setDueDate(businessCalendar.resolveDuedate((String) dueDate)); } else { throw new ActivitiIllegalArgumentException("Due date expression does not resolve to a Date or Date string: " + - taskDefinition.getDueDateExpression().getExpressionText()); + activeDueDateExpression.getExpressionText()); } } } - if (taskDefinition.getPriorityExpression() != null) { - final Object priority = taskDefinition.getPriorityExpression().getValue(execution); + if (activePriorityExpression != null) { + final Object priority = activePriorityExpression.getValue(execution); if (priority != null) { if (priority instanceof String) { try { @@ -114,36 +174,37 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { task.setPriority(((Number) priority).intValue()); } else { throw new ActivitiIllegalArgumentException("Priority expression does not resolve to a number: " + - taskDefinition.getPriorityExpression().getExpressionText()); + activePriorityExpression.getExpressionText()); } } } - if (taskDefinition.getCategoryExpression() != null) { - final Object category = taskDefinition.getCategoryExpression().getValue(execution); - if (category != null) { - if (category instanceof String) { - task.setCategory((String) category); - } else { - throw new ActivitiIllegalArgumentException("Category expression does not resolve to a string: " + - taskDefinition.getCategoryExpression().getExpressionText()); - } - } + if (activeCategoryExpression != null) { + final Object category = activeCategoryExpression.getValue(execution); + if (category != null) { + if (category instanceof String) { + task.setCategory((String) category); + } else { + throw new ActivitiIllegalArgumentException("Category expression does not resolve to a string: " + + activeCategoryExpression.getExpressionText()); + } + } } - if (taskDefinition.getFormKeyExpression() != null) { - final Object formKey = taskDefinition.getFormKeyExpression().getValue(execution); - if (formKey != null) { - if (formKey instanceof String) { - task.setFormKey((String) formKey); - } else { - throw new ActivitiIllegalArgumentException("FormKey expression does not resolve to a string: " + - taskDefinition.getFormKeyExpression().getExpressionText()); - } - } + if (activeFormKeyExpression != null) { + final Object formKey = activeFormKeyExpression.getValue(execution); + if (formKey != null) { + if (formKey instanceof String) { + task.setFormKey((String) formKey); + } else { + throw new ActivitiIllegalArgumentException("FormKey expression does not resolve to a string: " + + activeFormKeyExpression.getExpressionText()); + } + } } - handleAssignments(task, activityExecution); + handleAssignments(activeAssigneeExpression, activeOwnerExpression, activeCandidateUserExpressions, + activeCandidateGroupExpressions, task, activityExecution); // All properties set, now firing 'create' events if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) { @@ -168,9 +229,11 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { } @SuppressWarnings({ "unchecked", "rawtypes" }) - protected void handleAssignments(TaskEntity task, ActivityExecution execution) { - if (taskDefinition.getAssigneeExpression() != null) { - Object assigneeExpressionValue = taskDefinition.getAssigneeExpression().getValue(execution); + protected void handleAssignments(Expression assigneeExpression, Expression ownerExpression, Set candidateUserExpressions, + Set candidateGroupExpressions, TaskEntity task, ActivityExecution execution) { + + if (assigneeExpression != null) { + Object assigneeExpressionValue = assigneeExpression.getValue(execution); String assigneeValue = null; if (assigneeExpressionValue != null) { assigneeValue = assigneeExpressionValue.toString(); @@ -178,8 +241,8 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { task.setAssignee(assigneeValue, true, false); } - if (taskDefinition.getOwnerExpression() != null) { - Object ownerExpressionValue = taskDefinition.getOwnerExpression().getValue(execution); + if (ownerExpression != null) { + Object ownerExpressionValue = ownerExpression.getValue(execution); String ownerValue = null; if (ownerExpressionValue != null) { ownerValue = ownerExpressionValue.toString(); @@ -187,8 +250,8 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { task.setOwner(ownerValue); } - if (!taskDefinition.getCandidateGroupIdExpressions().isEmpty()) { - for (Expression groupIdExpr : taskDefinition.getCandidateGroupIdExpressions()) { + if (candidateGroupExpressions != null && !candidateGroupExpressions.isEmpty()) { + for (Expression groupIdExpr : candidateGroupExpressions) { Object value = groupIdExpr.getValue(execution); if (value instanceof String) { List candidates = extractCandidates((String) value); @@ -201,8 +264,8 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { } } - if (!taskDefinition.getCandidateUserIdExpressions().isEmpty()) { - for (Expression userIdExpr : taskDefinition.getCandidateUserIdExpressions()) { + if (candidateUserExpressions != null && !candidateUserExpressions.isEmpty()) { + for (Expression userIdExpr : candidateUserExpressions) { Object value = userIdExpr.getValue(execution); if (value instanceof String) { List candiates = extractCandidates((String) value); @@ -270,6 +333,40 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { return Arrays.asList(str.split("[\\s]*,[\\s]*")); } + protected Expression getActiveValue(Expression originalValue, String propertyName, ObjectNode taskElementProperties) { + Expression activeValue = originalValue; + if (taskElementProperties != null) { + JsonNode overrideValueNode = taskElementProperties.get(propertyName); + if (overrideValueNode != null) { + if (overrideValueNode.isNull()) { + activeValue = null; + } else { + activeValue = Context.getProcessEngineConfiguration().getExpressionManager().createExpression(overrideValueNode.asText()); + } + } + } + return activeValue; + } + + protected Set getActiveValueSet(Set originalValues, String propertyName, ObjectNode taskElementProperties) { + Set activeValues = originalValues; + if (taskElementProperties != null) { + JsonNode overrideValuesNode = taskElementProperties.get(propertyName); + if (overrideValuesNode != null) { + if (overrideValuesNode.isNull() || overrideValuesNode.isArray() == false || overrideValuesNode.size() == 0) { + activeValues = null; + } else { + ExpressionManager expressionManager = Context.getProcessEngineConfiguration().getExpressionManager(); + activeValues = new HashSet(); + for (JsonNode valueNode : overrideValuesNode) { + activeValues.add(expressionManager.createExpression(valueNode.asText())); + } + } + } + } + return activeValues; + } + // getters and setters ////////////////////////////////////////////////////// public TaskDefinition getTaskDefinition() { diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/deployer/BpmnDeployer.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/deployer/BpmnDeployer.java index bc97dfb575..442b1eb98b 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/deployer/BpmnDeployer.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/deployer/BpmnDeployer.java @@ -40,12 +40,16 @@ import org.activiti5.engine.impl.interceptor.CommandContext; import org.activiti5.engine.impl.jobexecutor.TimerDeclarationImpl; import org.activiti5.engine.impl.jobexecutor.TimerStartEventJobHandler; import org.activiti5.engine.impl.persistence.deploy.Deployer; +import org.activiti5.engine.impl.persistence.deploy.DeploymentManager; +import org.activiti5.engine.impl.persistence.deploy.ProcessDefinitionInfoCacheObject; import org.activiti5.engine.impl.persistence.entity.DeploymentEntity; import org.activiti5.engine.impl.persistence.entity.EventSubscriptionEntity; import org.activiti5.engine.impl.persistence.entity.IdentityLinkEntity; import org.activiti5.engine.impl.persistence.entity.MessageEventSubscriptionEntity; import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionEntityManager; +import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionInfoEntity; +import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionInfoEntityManager; import org.activiti5.engine.impl.persistence.entity.ResourceEntity; import org.activiti5.engine.impl.persistence.entity.SignalEventSubscriptionEntity; import org.activiti5.engine.impl.persistence.entity.TimerEntity; @@ -55,6 +59,9 @@ import org.activiti5.engine.task.IdentityLinkType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + /** * @author Tom Baeyens * @author Joram Barrez @@ -234,15 +241,54 @@ public class BpmnDeployer implements Deployer { } // Add to cache - processEngineConfiguration - .getDeploymentManager() - .getProcessDefinitionCache() - .add(processDefinition.getId(), processDefinition); + DeploymentManager deploymentManager = processEngineConfiguration.getDeploymentManager(); + deploymentManager.getProcessDefinitionCache().add(processDefinition.getId(), processDefinition); + addDefinitionInfoToCache(processDefinition, processEngineConfiguration, commandContext); // Add to deployment for further usage deployment.addDeployedArtifact(processDefinition); } } + + protected void addDefinitionInfoToCache(ProcessDefinitionEntity processDefinition, + ProcessEngineConfigurationImpl processEngineConfiguration, CommandContext commandContext) { + + if (processEngineConfiguration.isEnableProcessDefinitionInfoCache() == false) { + return; + } + + DeploymentManager deploymentManager = processEngineConfiguration.getDeploymentManager(); + ProcessDefinitionInfoEntityManager definitionInfoEntityManager = commandContext.getProcessDefinitionInfoEntityManager(); + ObjectMapper objectMapper = commandContext.getProcessEngineConfiguration().getObjectMapper(); + ProcessDefinitionInfoEntity definitionInfoEntity = definitionInfoEntityManager.findProcessDefinitionInfoByProcessDefinitionId(processDefinition.getId()); + + ObjectNode infoNode = null; + if (definitionInfoEntity != null && definitionInfoEntity.getInfoJsonId() != null) { + byte[] infoBytes = definitionInfoEntityManager.findInfoJsonById(definitionInfoEntity.getInfoJsonId()); + if (infoBytes != null) { + try { + infoNode = (ObjectNode) objectMapper.readTree(infoBytes); + } catch (Exception e) { + throw new ActivitiException("Error deserializing json info for process definition " + processDefinition.getId()); + } + } + } + + ProcessDefinitionInfoCacheObject definitionCacheObject = new ProcessDefinitionInfoCacheObject(); + if (definitionInfoEntity == null) { + definitionCacheObject.setRevision(0); + } else { + definitionCacheObject.setId(definitionInfoEntity.getId()); + definitionCacheObject.setRevision(definitionInfoEntity.getRevision()); + } + + if (infoNode == null) { + infoNode = objectMapper.createObjectNode(); + } + definitionCacheObject.setInfoNode(infoNode); + + deploymentManager.getProcessDefinitionInfoCache().add(processDefinition.getId(), definitionCacheObject); + } private void scheduleTimers(List timers) { for (TimerEntity timer : timers) { diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/helper/ClassDelegate.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/helper/ClassDelegate.java index 3e6bc74d91..d6f57fa1be 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/helper/ClassDelegate.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/helper/ClassDelegate.java @@ -19,6 +19,7 @@ import java.lang.reflect.Method; import java.util.List; import org.activiti.bpmn.model.MapExceptionEntry; +import org.activiti.engine.DynamicBpmnConstants; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.DelegateTask; import org.activiti.engine.delegate.ExecutionListener; @@ -39,6 +40,9 @@ import org.activiti5.engine.impl.pvm.delegate.ActivityExecution; import org.activiti5.engine.impl.pvm.delegate.SignallableActivityBehavior; import org.activiti5.engine.impl.pvm.delegate.SubProcessActivityBehavior; import org.activiti5.engine.impl.util.ReflectUtil; +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.databind.node.ObjectNode; /** @@ -59,6 +63,7 @@ public class ClassDelegate extends AbstractBpmnActivityBehavior implements TaskL protected ActivityBehavior activityBehaviorInstance; protected Expression skipExpression; protected List mapExceptions; + protected String serviceTaskId; public ClassDelegate(String className, List fieldDeclarations, Expression skipExpression) { this.className = className; @@ -67,10 +72,10 @@ public class ClassDelegate extends AbstractBpmnActivityBehavior implements TaskL } - public ClassDelegate(String className, List fieldDeclarations, Expression skipExpression, List mapExceptions) { + public ClassDelegate(String id, String className, List fieldDeclarations, Expression skipExpression, List mapExceptions) { this(className, fieldDeclarations, skipExpression); + this.serviceTaskId = id; this.mapExceptions = mapExceptions; - } public ClassDelegate(String className, List fieldDeclarations) { @@ -136,6 +141,17 @@ public class ClassDelegate extends AbstractBpmnActivityBehavior implements TaskL if (!isSkipExpressionEnabled || (isSkipExpressionEnabled && !SkipExpressionUtil.shouldSkipFlowElement(activityExecution, skipExpression))) { + if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) { + ObjectNode taskElementProperties = Context.getBpmnOverrideElementProperties(serviceTaskId, execution.getProcessDefinitionId()); + if (taskElementProperties != null && taskElementProperties.has(DynamicBpmnConstants.SERVICE_TASK_CLASS_NAME)) { + String overrideClassName = taskElementProperties.get(DynamicBpmnConstants.SERVICE_TASK_CLASS_NAME).asText(); + if (StringUtils.isNotEmpty(overrideClassName) && overrideClassName.equals(className) == false) { + className = overrideClassName; + activityBehaviorInstance = null; + } + } + } + if (activityBehaviorInstance == null) { activityBehaviorInstance = getActivityBehaviorInstance(activityExecution); } diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/parser/factory/DefaultActivityBehaviorFactory.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/parser/factory/DefaultActivityBehaviorFactory.java index fd6b2c712b..953dafb309 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/parser/factory/DefaultActivityBehaviorFactory.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/bpmn/parser/factory/DefaultActivityBehaviorFactory.java @@ -124,7 +124,7 @@ public class DefaultActivityBehaviorFactory extends AbstractBehaviorFactory impl } public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask, TaskDefinition taskDefinition) { - return new UserTaskActivityBehavior(taskDefinition); + return new UserTaskActivityBehavior(userTask.getId(), taskDefinition); } // Service task @@ -136,7 +136,8 @@ public class DefaultActivityBehaviorFactory extends AbstractBehaviorFactory impl } else { skipExpression = null; } - return new ClassDelegate(serviceTask.getImplementation(), createFieldDeclarations(serviceTask.getFieldExtensions()), skipExpression, serviceTask.getMapExceptions()); + return new ClassDelegate(serviceTask.getId(), serviceTask.getImplementation(), createFieldDeclarations(serviceTask.getFieldExtensions()), + skipExpression, serviceTask.getMapExceptions()); } public ServiceTaskDelegateExpressionActivityBehavior createServiceTaskDelegateExpressionActivityBehavior(ServiceTask serviceTask) { @@ -147,7 +148,7 @@ public class DefaultActivityBehaviorFactory extends AbstractBehaviorFactory impl } else { skipExpression = null; } - return new ServiceTaskDelegateExpressionActivityBehavior(delegateExpression, skipExpression, createFieldDeclarations(serviceTask.getFieldExtensions())); + return new ServiceTaskDelegateExpressionActivityBehavior(serviceTask.getId(), delegateExpression, skipExpression, createFieldDeclarations(serviceTask.getFieldExtensions())); } public ServiceTaskExpressionActivityBehavior createServiceTaskExpressionActivityBehavior(ServiceTask serviceTask) { @@ -158,7 +159,7 @@ public class DefaultActivityBehaviorFactory extends AbstractBehaviorFactory impl } else { skipExpression = null; } - return new ServiceTaskExpressionActivityBehavior(expression, skipExpression, serviceTask.getResultVariableName()); + return new ServiceTaskExpressionActivityBehavior(serviceTask.getId(), expression, skipExpression, serviceTask.getResultVariableName()); } public WebServiceActivityBehavior createWebServiceActivityBehavior(ServiceTask serviceTask) { diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cfg/ProcessEngineConfigurationImpl.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cfg/ProcessEngineConfigurationImpl.java index 9b37b08e93..7d8ddfe201 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cfg/ProcessEngineConfigurationImpl.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cfg/ProcessEngineConfigurationImpl.java @@ -43,6 +43,7 @@ import org.activiti.image.impl.DefaultProcessDiagramGenerator; import org.activiti.validation.ProcessValidator; import org.activiti.validation.ProcessValidatorFactory; import org.activiti5.engine.ActivitiException; +import org.activiti5.engine.DynamicBpmnService; import org.activiti5.engine.FormService; import org.activiti5.engine.HistoryService; import org.activiti5.engine.IdentityService; @@ -58,6 +59,7 @@ import org.activiti5.engine.delegate.event.ActivitiEventListener; import org.activiti5.engine.delegate.event.ActivitiEventType; import org.activiti5.engine.delegate.event.impl.ActivitiEventDispatcherImpl; import org.activiti5.engine.form.AbstractFormType; +import org.activiti5.engine.impl.DynamicBpmnServiceImpl; import org.activiti5.engine.impl.FormServiceImpl; import org.activiti5.engine.impl.HistoryServiceImpl; import org.activiti5.engine.impl.IdentityServiceImpl; @@ -165,6 +167,7 @@ import org.activiti5.engine.impl.persistence.deploy.DefaultDeploymentCache; import org.activiti5.engine.impl.persistence.deploy.Deployer; import org.activiti5.engine.impl.persistence.deploy.DeploymentCache; import org.activiti5.engine.impl.persistence.deploy.DeploymentManager; +import org.activiti5.engine.impl.persistence.deploy.ProcessDefinitionInfoCache; import org.activiti5.engine.impl.persistence.entity.AttachmentEntityManager; import org.activiti5.engine.impl.persistence.entity.ByteArrayEntityManager; import org.activiti5.engine.impl.persistence.entity.CommentEntityManager; @@ -184,6 +187,7 @@ import org.activiti5.engine.impl.persistence.entity.JobEntityManager; import org.activiti5.engine.impl.persistence.entity.ModelEntityManager; import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionEntityManager; +import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionInfoEntityManager; import org.activiti5.engine.impl.persistence.entity.PropertyEntityManager; import org.activiti5.engine.impl.persistence.entity.ResourceEntityManager; import org.activiti5.engine.impl.persistence.entity.TableDataManager; @@ -234,6 +238,8 @@ import org.apache.ibatis.type.JdbcType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.databind.ObjectMapper; + /** * @author Tom Baeyens @@ -259,6 +265,7 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig protected TaskService taskService = new TaskServiceImpl(this); protected FormService formService = new FormServiceImpl(); protected ManagementService managementService = new ManagementServiceImpl(); + protected DynamicBpmnService dynamicBpmnService = new DynamicBpmnServiceImpl(this); // COMMAND EXECUTORS //////////////////////////////////////////////////////// @@ -301,6 +308,8 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig protected DeploymentCache processDefinitionCache; protected int bpmnModelCacheLimit = -1; // By default, no limit protected DeploymentCache bpmnModelCache; + protected int processDefinitionInfoCacheLimit = -1; // By default, no limit + protected ProcessDefinitionInfoCache processDefinitionInfoCache; protected int knowledgeBaseCacheLimit = -1; protected DeploymentCache knowledgeBaseCache; @@ -407,6 +416,8 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig */ protected int maxNrOfStatementsInBulkInsert = 100; + protected ObjectMapper objectMapper = new ObjectMapper(); + protected boolean enableEventDispatcher = true; protected ActivitiEventDispatcher eventDispatcher; protected List eventListeners; @@ -421,6 +432,8 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig */ protected int maxLengthStringVariableType = 4000; + protected boolean enableProcessDefinitionInfoCache = false; + // Activiti 5 backwards compatibility handler protected Activiti5CompatibilityHandler activiti5CompatibilityHandler; @@ -561,6 +574,7 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig initService(taskService); initService(formService); initService(managementService); + initService(dynamicBpmnService); } protected void initService(Object service) { @@ -838,6 +852,7 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig addSessionFactory(new GenericManagerFactory(IdentityLinkEntityManager.class)); addSessionFactory(new GenericManagerFactory(JobEntityManager.class)); addSessionFactory(new GenericManagerFactory(ProcessDefinitionEntityManager.class)); + addSessionFactory(new GenericManagerFactory(ProcessDefinitionInfoEntityManager.class)); addSessionFactory(new GenericManagerFactory(PropertyEntityManager.class)); addSessionFactory(new GenericManagerFactory(ResourceEntityManager.class)); addSessionFactory(new GenericManagerFactory(ByteArrayEntityManager.class)); @@ -973,6 +988,14 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig } } + if (processDefinitionInfoCache == null) { + if (processDefinitionInfoCacheLimit <= 0) { + processDefinitionInfoCache = new ProcessDefinitionInfoCache(commandExecutor); + } else { + processDefinitionInfoCache = new ProcessDefinitionInfoCache(commandExecutor, processDefinitionInfoCacheLimit); + } + } + // Knowledge base cache (used for Drools business task) if (knowledgeBaseCache == null) { if (knowledgeBaseCacheLimit <= 0) { @@ -984,6 +1007,7 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig deploymentManager.setProcessDefinitionCache(processDefinitionCache); deploymentManager.setBpmnModelCache(bpmnModelCache); + deploymentManager.setProcessDefinitionInfoCache(processDefinitionInfoCache); deploymentManager.setKnowledgeBaseCache(knowledgeBaseCache); } } @@ -1573,6 +1597,10 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig return managementService; } + public DynamicBpmnService getDynamicBpmnService() { + return dynamicBpmnService; + } + public ProcessEngineConfigurationImpl setManagementService(ManagementService managementService) { this.managementService = managementService; return this; @@ -2127,6 +2155,10 @@ public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfig public void setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) { this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert; } + + public ObjectMapper getObjectMapper() { + return objectMapper; + } public Activiti5CompatibilityHandler getActiviti5CompatibilityHandler() { return activiti5CompatibilityHandler; diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cmd/GetProcessDefinitionInfoCmd.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cmd/GetProcessDefinitionInfoCmd.java new file mode 100644 index 0000000000..4805c05759 --- /dev/null +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cmd/GetProcessDefinitionInfoCmd.java @@ -0,0 +1,56 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.activiti5.engine.impl.cmd; + +import java.io.Serializable; + +import org.activiti5.engine.ActivitiIllegalArgumentException; +import org.activiti5.engine.impl.interceptor.Command; +import org.activiti5.engine.impl.interceptor.CommandContext; +import org.activiti5.engine.impl.persistence.deploy.DeploymentManager; +import org.activiti5.engine.impl.persistence.deploy.ProcessDefinitionInfoCacheObject; + +import com.fasterxml.jackson.databind.node.ObjectNode; + + +/** + * @author Tijs Rademakers + */ +public class GetProcessDefinitionInfoCmd implements Command, Serializable { + + private static final long serialVersionUID = 1L; + + protected String processDefinitionId; + + public GetProcessDefinitionInfoCmd(String processDefinitionId) { + this.processDefinitionId = processDefinitionId; + } + + public ObjectNode execute(CommandContext commandContext) { + if (processDefinitionId == null) { + throw new ActivitiIllegalArgumentException("process definition id is null"); + } + + ObjectNode resultNode = null; + DeploymentManager deploymentManager = commandContext.getProcessEngineConfiguration().getDeploymentManager(); + // make sure the process definition is in the cache + deploymentManager.findDeployedProcessDefinitionById(processDefinitionId); + ProcessDefinitionInfoCacheObject definitionInfoCacheObject = deploymentManager.getProcessDefinitionInfoCache().get(processDefinitionId); + if (definitionInfoCacheObject != null) { + resultNode = definitionInfoCacheObject.getInfoNode(); + } + + return resultNode; + } + +} \ No newline at end of file diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cmd/SaveProcessDefinitionInfoCmd.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cmd/SaveProcessDefinitionInfoCmd.java new file mode 100644 index 0000000000..354bf995fe --- /dev/null +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/cmd/SaveProcessDefinitionInfoCmd.java @@ -0,0 +1,74 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.activiti5.engine.impl.cmd; + +import java.io.Serializable; + +import org.activiti5.engine.ActivitiException; +import org.activiti5.engine.ActivitiIllegalArgumentException; +import org.activiti5.engine.impl.interceptor.Command; +import org.activiti5.engine.impl.interceptor.CommandContext; +import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionInfoEntity; +import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionInfoEntityManager; + +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.node.ObjectNode; + + +/** + * @author Tijs Rademakers + */ +public class SaveProcessDefinitionInfoCmd implements Command, Serializable { + + private static final long serialVersionUID = 1L; + + protected String processDefinitionId; + protected ObjectNode infoNode; + + public SaveProcessDefinitionInfoCmd(String processDefinitionId, ObjectNode infoNode) { + this.processDefinitionId = processDefinitionId; + this.infoNode = infoNode; + } + + public Void execute(CommandContext commandContext) { + if (processDefinitionId == null) { + throw new ActivitiIllegalArgumentException("process definition id is null"); + } + + if (infoNode == null) { + throw new ActivitiIllegalArgumentException("process definition info node is null"); + } + + ProcessDefinitionInfoEntityManager definitionInfoEntityManager = commandContext.getProcessDefinitionInfoEntityManager(); + ProcessDefinitionInfoEntity definitionInfoEntity = definitionInfoEntityManager.findProcessDefinitionInfoByProcessDefinitionId(processDefinitionId); + if (definitionInfoEntity == null) { + definitionInfoEntity = new ProcessDefinitionInfoEntity(); + definitionInfoEntity.setProcessDefinitionId(processDefinitionId); + commandContext.getProcessDefinitionInfoEntityManager().insertProcessDefinitionInfo(definitionInfoEntity); + } else { + commandContext.getProcessDefinitionInfoEntityManager().updateProcessDefinitionInfo(definitionInfoEntity); + } + + if (infoNode != null) { + try { + ObjectWriter writer = commandContext.getProcessEngineConfiguration().getObjectMapper().writer(); + commandContext.getProcessDefinitionInfoEntityManager().updateInfoJson(definitionInfoEntity.getId(), writer.writeValueAsBytes(infoNode)); + } catch (Exception e) { + throw new ActivitiException("Unable to serialize info node " + infoNode); + } + } + + return null; + } + +} \ No newline at end of file diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/context/Context.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/context/Context.java index 91c87766eb..023f9c108f 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/context/Context.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/context/Context.java @@ -13,13 +13,18 @@ package org.activiti5.engine.impl.context; +import java.util.HashMap; +import java.util.Map; import java.util.Stack; import org.activiti5.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.activiti5.engine.impl.interceptor.CommandContext; import org.activiti5.engine.impl.jobexecutor.JobExecutorContext; +import org.activiti5.engine.impl.persistence.deploy.ProcessDefinitionInfoCacheObject; import org.activiti5.engine.impl.pvm.runtime.InterpretableExecution; +import com.fasterxml.jackson.databind.node.ObjectNode; + /** * @author Tom Baeyens @@ -31,7 +36,8 @@ public class Context { protected static ThreadLocal> processEngineConfigurationStackThreadLocal = new ThreadLocal>(); protected static ThreadLocal> executionContextStackThreadLocal = new ThreadLocal>(); protected static ThreadLocal jobExecutorContextThreadLocal = new ThreadLocal(); - + protected static ThreadLocal> bpmnOverrideContextThreadLocal = new ThreadLocal>(); + public static CommandContext getCommandContext() { Stack stack = getStack(commandContextThreadLocal); if (stack.isEmpty()) { @@ -101,4 +107,57 @@ public class Context { public static void removeJobExecutorContext() { jobExecutorContextThreadLocal.remove(); } + + public static ObjectNode getBpmnOverrideElementProperties(String id, String processDefinitionId) { + ObjectNode definitionInfoNode = getProcessDefinitionInfoNode(processDefinitionId); + ObjectNode elementProperties = null; + if (definitionInfoNode != null) { + elementProperties = getProcessEngineConfiguration().getDynamicBpmnService().getBpmnElementProperties(id, definitionInfoNode); + } + return elementProperties; + } + + public static ObjectNode getLocalizationElementProperties(String language, String id, String processDefinitionId) { + ObjectNode definitionInfoNode = getProcessDefinitionInfoNode(processDefinitionId); + ObjectNode localizationProperties = null; + if (definitionInfoNode != null) { + localizationProperties = getProcessEngineConfiguration().getDynamicBpmnService().getLocalizationElementProperties( + language, id, definitionInfoNode); + } + return localizationProperties; + } + + public static void removeBpmnOverrideContext() { + bpmnOverrideContextThreadLocal.remove(); + } + + protected static ObjectNode getProcessDefinitionInfoNode(String processDefinitionId) { + Map bpmnOverrideMap = getBpmnOverrideContext(); + if (bpmnOverrideMap.containsKey(processDefinitionId) == false) { + ProcessDefinitionInfoCacheObject cacheObject = getProcessEngineConfiguration().getDeploymentManager() + .getProcessDefinitionInfoCache() + .get(processDefinitionId); + + addBpmnOverrideElement(processDefinitionId, cacheObject.getInfoNode()); + } + + return getBpmnOverrideContext().get(processDefinitionId); + } + + protected static Map getBpmnOverrideContext() { + Map bpmnOverrideMap = bpmnOverrideContextThreadLocal.get(); + if (bpmnOverrideMap == null) { + bpmnOverrideMap = new HashMap(); + } + return bpmnOverrideMap; + } + + protected static void addBpmnOverrideElement(String id, ObjectNode infoNode) { + Map bpmnOverrideMap = bpmnOverrideContextThreadLocal.get(); + if (bpmnOverrideMap == null) { + bpmnOverrideMap = new HashMap(); + bpmnOverrideContextThreadLocal.set(bpmnOverrideMap); + } + bpmnOverrideMap.put(id, infoNode); + } } diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/history/DefaultHistoryManager.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/history/DefaultHistoryManager.java index feea6c939e..fc0ea19420 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/history/DefaultHistoryManager.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/history/DefaultHistoryManager.java @@ -17,11 +17,11 @@ import java.util.Date; import java.util.List; import java.util.Map; +import org.activiti.engine.delegate.Expression; import org.activiti5.engine.history.HistoricActivityInstance; import org.activiti5.engine.impl.HistoricActivityInstanceQueryImpl; import org.activiti5.engine.impl.cfg.IdGenerator; import org.activiti5.engine.impl.context.Context; -import org.activiti5.engine.impl.form.TaskFormHandler; import org.activiti5.engine.impl.identity.Authentication; import org.activiti5.engine.impl.persistence.AbstractManager; import org.activiti5.engine.impl.persistence.entity.CommentEntity; @@ -559,13 +559,11 @@ public void recordTaskDefinitionKeyChange(TaskEntity task, String taskDefinition historicTaskInstance.setTaskDefinitionKey(taskDefinitionKey); if (taskDefinitionKey != null) { - TaskFormHandler taskFormHandler = task.getTaskDefinition().getTaskFormHandler(); - if (taskFormHandler != null) { - if (taskFormHandler.getFormKey() != null) { - Object formValue = taskFormHandler.getFormKey().getValue(task.getExecution()); - if (formValue != null) { - historicTaskInstance.setFormKey(formValue.toString()); - } + Expression taskFormExpression = task.getTaskDefinition().getFormKeyExpression(); + if (taskFormExpression != null) { + Object formValue = taskFormExpression.getValue(task.getExecution()); + if (formValue != null) { + historicTaskInstance.setFormKey(formValue.toString()); } } } diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/interceptor/CommandContext.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/interceptor/CommandContext.java index f6e49d6f09..aa385c0ac8 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/interceptor/CommandContext.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/interceptor/CommandContext.java @@ -49,6 +49,7 @@ import org.activiti5.engine.impl.persistence.entity.JobEntityManager; import org.activiti5.engine.impl.persistence.entity.MembershipIdentityManager; import org.activiti5.engine.impl.persistence.entity.ModelEntityManager; import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionEntityManager; +import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionInfoEntityManager; import org.activiti5.engine.impl.persistence.entity.PropertyEntityManager; import org.activiti5.engine.impl.persistence.entity.ResourceEntityManager; import org.activiti5.engine.impl.persistence.entity.TableDataManager; @@ -283,6 +284,10 @@ public class CommandContext { return getSession(ProcessDefinitionEntityManager.class); } + public ProcessDefinitionInfoEntityManager getProcessDefinitionInfoEntityManager() { + return getSession(ProcessDefinitionInfoEntityManager.class); + } + public ModelEntityManager getModelEntityManager() { return getSession(ModelEntityManager.class); } diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/interceptor/CommandContextInterceptor.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/interceptor/CommandContextInterceptor.java index 85756722ef..7c961570fd 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/interceptor/CommandContextInterceptor.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/interceptor/CommandContextInterceptor.java @@ -70,6 +70,7 @@ public class CommandContextInterceptor extends AbstractCommandInterceptor { // Pop from stack Context.removeCommandContext(); Context.removeProcessEngineConfiguration(); + Context.removeBpmnOverrideContext(); // don't remove the fallback because it's needed in Activiti 6 code like the Camel module //org.activiti.engine.impl.context.Context.removeFallbackActiviti5CompatibilityHandler(); } diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/AbstractManager.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/AbstractManager.java index a52dcadcdd..839e6d3020 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/AbstractManager.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/AbstractManager.java @@ -36,6 +36,7 @@ import org.activiti5.engine.impl.persistence.entity.IdentityLinkEntityManager; import org.activiti5.engine.impl.persistence.entity.MembershipIdentityManager; import org.activiti5.engine.impl.persistence.entity.ModelEntityManager; import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionEntityManager; +import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionInfoEntityManager; import org.activiti5.engine.impl.persistence.entity.ResourceEntityManager; import org.activiti5.engine.impl.persistence.entity.TaskEntityManager; import org.activiti5.engine.impl.persistence.entity.UserIdentityManager; @@ -80,6 +81,10 @@ public abstract class AbstractManager implements Session { return getSession(ProcessDefinitionEntityManager.class); } + protected ProcessDefinitionInfoEntityManager getProcessDefinitionInfoManager() { + return getSession(ProcessDefinitionInfoEntityManager.class); + } + protected ModelEntityManager getModelManager() { return getSession(ModelEntityManager.class); } diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/DeploymentManager.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/DeploymentManager.java index 856df80c9a..da43799c89 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/DeploymentManager.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/DeploymentManager.java @@ -45,6 +45,7 @@ public class DeploymentManager { protected DeploymentCache processDefinitionCache; protected DeploymentCache bpmnModelCache; + protected ProcessDefinitionInfoCache processDefinitionInfoCache; protected DeploymentCache knowledgeBaseCache; // Needs to be object to avoid an import to Drools in this core class protected List deployers; @@ -236,6 +237,14 @@ public class DeploymentManager { this.bpmnModelCache = bpmnModelCache; } + public ProcessDefinitionInfoCache getProcessDefinitionInfoCache() { + return processDefinitionInfoCache; + } + + public void setProcessDefinitionInfoCache(ProcessDefinitionInfoCache processDefinitionInfoCache) { + this.processDefinitionInfoCache = processDefinitionInfoCache; + } + public DeploymentCache getKnowledgeBaseCache() { return knowledgeBaseCache; } diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/ProcessDefinitionInfoCache.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/ProcessDefinitionInfoCache.java new file mode 100644 index 0000000000..e93813ccd7 --- /dev/null +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/ProcessDefinitionInfoCache.java @@ -0,0 +1,121 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.activiti5.engine.impl.persistence.deploy; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.activiti5.engine.ActivitiException; +import org.activiti5.engine.impl.interceptor.Command; +import org.activiti5.engine.impl.interceptor.CommandContext; +import org.activiti5.engine.impl.interceptor.CommandExecutor; +import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionInfoEntity; +import org.activiti5.engine.impl.persistence.entity.ProcessDefinitionInfoEntityManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Default cache: keep everything in memory, unless a limit is set. + * + * @author Tijs Rademakers + */ +public class ProcessDefinitionInfoCache { + + private static final Logger logger = LoggerFactory.getLogger(ProcessDefinitionInfoCache.class); + + protected Map cache; + protected CommandExecutor commandExecutor; + + /** Cache with no limit */ + public ProcessDefinitionInfoCache(CommandExecutor commandExecutor) { + this.commandExecutor = commandExecutor; + this.cache = new HashMap(); + } + + /** Cache which has a hard limit: no more elements will be cached than the limit. */ + public ProcessDefinitionInfoCache(CommandExecutor commandExecutor, final int limit) { + this.commandExecutor = commandExecutor; + this.cache = Collections.synchronizedMap(new LinkedHashMap(limit + 1, 0.75f, true) { + // +1 is needed, because the entry is inserted first, before it is removed + // 0.75 is the default (see javadocs) + // true will keep the 'access-order', which is needed to have a real LRU cache + private static final long serialVersionUID = 1L; + + protected boolean removeEldestEntry(Map.Entry eldest) { + boolean removeEldest = size() > limit; + if (removeEldest) { + logger.trace("Cache limit is reached, {} will be evicted", eldest.getKey()); + } + return removeEldest; + } + + }); + } + + public ProcessDefinitionInfoCacheObject get(final String processDefinitionId) { + ProcessDefinitionInfoCacheObject infoCacheObject = null; + if (cache.containsKey(processDefinitionId)) { + infoCacheObject = commandExecutor.execute(new Command() { + + @Override + public ProcessDefinitionInfoCacheObject execute(CommandContext commandContext) { + ProcessDefinitionInfoEntityManager infoEntityManager = commandContext.getProcessDefinitionInfoEntityManager(); + ObjectMapper objectMapper = commandContext.getProcessEngineConfiguration().getObjectMapper(); + + ProcessDefinitionInfoCacheObject cacheObject = cache.get(processDefinitionId); + ProcessDefinitionInfoEntity infoEntity = infoEntityManager.findProcessDefinitionInfoByProcessDefinitionId(processDefinitionId); + if (infoEntity != null && infoEntity.getRevision() != cacheObject.getRevision()) { + cacheObject.setRevision(infoEntity.getRevision()); + if (infoEntity.getInfoJsonId() != null) { + byte[] infoBytes = infoEntityManager.findInfoJsonById(infoEntity.getInfoJsonId()); + try { + ObjectNode infoNode = (ObjectNode) objectMapper.readTree(infoBytes); + cacheObject.setInfoNode(infoNode); + } catch (Exception e) { + throw new ActivitiException("Error reading json info node for process definition " + processDefinitionId, e); + } + } + } else if (infoEntity == null) { + cacheObject.setRevision(0); + cacheObject.setInfoNode(objectMapper.createObjectNode()); + } + return cacheObject; + } + }); + } + return infoCacheObject; + } + + public void add(String id, ProcessDefinitionInfoCacheObject obj) { + cache.put(id, obj); + } + + public void remove(String id) { + cache.remove(id); + } + + public void clear() { + cache.clear(); + } + + // For testing purposes only + public int size() { + return cache.size(); + } + +} \ No newline at end of file diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/ProcessDefinitionInfoCacheObject.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/ProcessDefinitionInfoCacheObject.java new file mode 100644 index 0000000000..00df482970 --- /dev/null +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/deploy/ProcessDefinitionInfoCacheObject.java @@ -0,0 +1,49 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.activiti5.engine.impl.persistence.deploy; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * @author Tijs Rademakers + */ +public class ProcessDefinitionInfoCacheObject { + + protected String id; + protected int revision; + protected ObjectNode infoNode; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getRevision() { + return revision; + } + + public void setRevision(int revision) { + this.revision = revision; + } + + public ObjectNode getInfoNode() { + return infoNode; + } + + public void setInfoNode(ObjectNode infoNode) { + this.infoNode = infoNode; + } +} \ No newline at end of file diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/DeploymentEntityManager.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/DeploymentEntityManager.java index dc1ad91e4e..69dabf521a 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/DeploymentEntityManager.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/DeploymentEntityManager.java @@ -74,7 +74,6 @@ public class DeploymentEntityManager extends AbstractManager { getProcessInstanceManager() .deleteProcessInstancesByProcessDefinition(processDefinitionId, "deleted deployment", cascade); - } } @@ -85,6 +84,8 @@ public class DeploymentEntityManager extends AbstractManager { // event subscriptions getEventSubscriptionManager().deleteEventSubscriptionsForProcessDefinition(processDefinitionId); + + getProcessDefinitionInfoManager().deleteProcessDefinitionInfo(processDefinitionId); } // delete process definitions from db diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/ProcessDefinitionInfoEntity.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/ProcessDefinitionInfoEntity.java new file mode 100644 index 0000000000..ec122b6c49 --- /dev/null +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/ProcessDefinitionInfoEntity.java @@ -0,0 +1,80 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine.impl.persistence.entity; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.activiti5.engine.impl.db.HasRevision; +import org.activiti5.engine.impl.db.PersistentObject; + + +/** + * @author Tijs Rademakers + */ +public class ProcessDefinitionInfoEntity implements HasRevision, PersistentObject, Serializable { + + private static final long serialVersionUID = 1L; + + protected String id; + protected String processDefinitionId; + protected int revision = 1; + protected String infoJsonId; + + public Object getPersistentState() { + Map persistentState = new HashMap(); + persistentState.put("processDefinitionId", this.processDefinitionId); + persistentState.put("infoJsonId", this.infoJsonId); + return persistentState; + } + + // getters and setters ////////////////////////////////////////////////////// + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getProcessDefinitionId() { + return processDefinitionId; + } + + public void setProcessDefinitionId(String processDefinitionId) { + this.processDefinitionId = processDefinitionId; + } + + public int getRevision() { + return revision; + } + + public int getRevisionNext() { + return revision + 1; + } + + public void setRevision(int revision) { + this.revision = revision; + } + + public String getInfoJsonId() { + return infoJsonId; + } + + public void setInfoJsonId(String infoJsonId) { + this.infoJsonId = infoJsonId; + } +} \ No newline at end of file diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/ProcessDefinitionInfoEntityManager.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/ProcessDefinitionInfoEntityManager.java new file mode 100644 index 0000000000..df6496f80a --- /dev/null +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/persistence/entity/ProcessDefinitionInfoEntityManager.java @@ -0,0 +1,97 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine.impl.persistence.entity; + +import org.activiti5.engine.delegate.event.ActivitiEventType; +import org.activiti5.engine.delegate.event.impl.ActivitiEventBuilder; +import org.activiti5.engine.impl.context.Context; +import org.activiti5.engine.impl.db.DbSqlSession; +import org.activiti5.engine.impl.db.PersistentObject; +import org.activiti5.engine.impl.interceptor.CommandContext; +import org.activiti5.engine.impl.persistence.AbstractManager; + + +/** + * @author Tijs Rademakers + */ +public class ProcessDefinitionInfoEntityManager extends AbstractManager { + + public void insertProcessDefinitionInfo(ProcessDefinitionInfoEntity processDefinitionInfo) { + getDbSqlSession().insert((PersistentObject) processDefinitionInfo); + + if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) { + Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent( + ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_CREATED, processDefinitionInfo)); + Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent( + ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_INITIALIZED, processDefinitionInfo)); + } + } + + public void updateProcessDefinitionInfo(ProcessDefinitionInfoEntity updatedProcessDefinitionInfo) { + CommandContext commandContext = Context.getCommandContext(); + DbSqlSession dbSqlSession = commandContext.getDbSqlSession(); + dbSqlSession.update(updatedProcessDefinitionInfo); + + if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) { + Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent( + ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_UPDATED, updatedProcessDefinitionInfo)); + } + } + + public void deleteProcessDefinitionInfo(String processDefinitionId) { + ProcessDefinitionInfoEntity processDefinitionInfo = findProcessDefinitionInfoByProcessDefinitionId(processDefinitionId); + if (processDefinitionInfo != null) { + getDbSqlSession().delete(processDefinitionInfo); + deleteInfoJson(processDefinitionInfo); + + if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) { + Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent( + ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_DELETED, processDefinitionInfo)); + } + } + } + + public void updateInfoJson(String id, byte[] json) { + ProcessDefinitionInfoEntity processDefinitionInfo = getDbSqlSession().selectById(ProcessDefinitionInfoEntity.class, id); + if (processDefinitionInfo != null) { + ByteArrayRef ref = new ByteArrayRef(processDefinitionInfo.getInfoJsonId()); + ref.setValue("json", json); + + if (processDefinitionInfo.getInfoJsonId() == null) { + processDefinitionInfo.setInfoJsonId(ref.getId()); + updateProcessDefinitionInfo(processDefinitionInfo); + } + } + } + + public void deleteInfoJson(ProcessDefinitionInfoEntity processDefinitionInfo) { + if (processDefinitionInfo.getInfoJsonId() != null) { + ByteArrayRef ref = new ByteArrayRef(processDefinitionInfo.getInfoJsonId()); + ref.delete(); + } + } + + public ProcessDefinitionInfoEntity findProcessDefinitionInfoById(String id) { + return (ProcessDefinitionInfoEntity) getDbSqlSession().selectOne("selectProcessDefinitionInfo", id); + } + + public ProcessDefinitionInfoEntity findProcessDefinitionInfoByProcessDefinitionId(String processDefinitionId) { + return (ProcessDefinitionInfoEntity) getDbSqlSession().selectOne("selectProcessDefinitionInfoByProcessDefinitionId", processDefinitionId); + } + + public byte[] findInfoJsonById(String infoJsonId) { + ByteArrayRef ref = new ByteArrayRef(infoJsonId); + return ref.getBytes(); + } +} \ No newline at end of file diff --git a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/task/TaskDefinition.java b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/task/TaskDefinition.java index cb84295abc..bb5c4ffb6b 100644 --- a/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/task/TaskDefinition.java +++ b/modules/activiti5-engine/src/main/java/org/activiti5/engine/impl/task/TaskDefinition.java @@ -101,6 +101,10 @@ public class TaskDefinition implements Serializable { public void addCandidateUserIdExpression(Expression userId) { candidateUserIdExpressions.add(userId); } + + public void setCandidateUserIdExpressions(Set candidateUserIdExpressions) { + this.candidateUserIdExpressions = candidateUserIdExpressions; + } public Set getCandidateGroupIdExpressions() { return candidateGroupIdExpressions; @@ -109,6 +113,10 @@ public class TaskDefinition implements Serializable { public void addCandidateGroupIdExpression(Expression groupId) { candidateGroupIdExpressions.add(groupId); } + + public void setCandidateGroupIdExpressions(Set candidateGroupIdExpressions) { + this.candidateGroupIdExpressions = candidateGroupIdExpressions; + } public Map> getCustomUserIdentityLinkExpressions() { return customUserIdentityLinkExpressions; diff --git a/modules/activiti5-engine/src/main/resources/org/activiti5/db/mapping/entity/ProcessDefinitionInfo.xml b/modules/activiti5-engine/src/main/resources/org/activiti5/db/mapping/entity/ProcessDefinitionInfo.xml new file mode 100644 index 0000000000..c611778691 --- /dev/null +++ b/modules/activiti5-engine/src/main/resources/org/activiti5/db/mapping/entity/ProcessDefinitionInfo.xml @@ -0,0 +1,74 @@ + + + + + + + + + + insert into ${prefix}ACT_PROCDEF_INFO(ID_, PROC_DEF_ID_, REV_, INFO_JSON_ID_) + values(#{id, jdbcType=VARCHAR}, + #{processDefinitionId, jdbcType=VARCHAR}, + 1, + #{infoJsonId, jdbcType=VARCHAR}) + + + + INSERT INTO ${prefix}ACT_PROCDEF_INFO(ID_, PROC_DEF_ID_, REV_, INFO_JSON_ID_) + VALUES + + (#{processDefinitionInfo.id, jdbcType=VARCHAR}, + #{processDefinitionInfo.processDefinitionId, jdbcType=VARCHAR}, + 1, + #{processDefinitionInfo.infoJsonId, jdbcType=VARCHAR}) + + + + + INSERT ALL + + INTO ${prefix}ACT_PROCDEF_INFO(ID_, PROC_DEF_ID_, REV_, INFO_JSON_ID_) VALUES + (#{processDefinitionInfo.id, jdbcType=VARCHAR}, + #{processDefinitionInfo.processDefinitionId, jdbcType=VARCHAR}, + 1, + #{processDefinitionInfo.infoJsonId, jdbcType=VARCHAR}) + + SELECT * FROM dual + + + + + update ${prefix}ACT_PROCDEF_INFO set + REV_ = #{revisionNext, jdbcType=INTEGER}, + INFO_JSON_ID_ = #{infoJsonId, jdbcType=VARCHAR} + where ID_ = #{id, jdbcType=VARCHAR} and REV_ = #{revision, jdbcType=INTEGER} + + + + + + + delete from ${prefix}ACT_PROCDEF_INFO where ID_ = #{id} and REV_ = #{revision} + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/activiti5-engine/src/main/resources/org/activiti5/db/mapping/mappings.xml b/modules/activiti5-engine/src/main/resources/org/activiti5/db/mapping/mappings.xml index d551dbc0a2..ee449ed04d 100644 --- a/modules/activiti5-engine/src/main/resources/org/activiti5/db/mapping/mappings.xml +++ b/modules/activiti5-engine/src/main/resources/org/activiti5/db/mapping/mappings.xml @@ -33,6 +33,7 @@ + diff --git a/modules/activiti5-test/src/main/java/org/activiti5/engine/impl/test/AbstractActivitiTestCase.java b/modules/activiti5-test/src/main/java/org/activiti5/engine/impl/test/AbstractActivitiTestCase.java index 419a611b51..66c23770b5 100644 --- a/modules/activiti5-test/src/main/java/org/activiti5/engine/impl/test/AbstractActivitiTestCase.java +++ b/modules/activiti5-test/src/main/java/org/activiti5/engine/impl/test/AbstractActivitiTestCase.java @@ -26,6 +26,7 @@ import org.activiti.bpmn.model.EndEvent; import org.activiti.bpmn.model.SequenceFlow; import org.activiti.bpmn.model.StartEvent; import org.activiti.bpmn.model.UserTask; +import org.activiti.engine.DynamicBpmnService; import org.activiti.engine.FormService; import org.activiti.engine.HistoryService; import org.activiti.engine.IdentityService; @@ -70,6 +71,7 @@ public abstract class AbstractActivitiTestCase extends PvmTestCase { protected HistoryService historyService; protected IdentityService identityService; protected ManagementService managementService; + protected DynamicBpmnService dynamicBpmnService; @Override protected void setUp() throws Exception { @@ -184,6 +186,7 @@ public abstract class AbstractActivitiTestCase extends PvmTestCase { historyService = processEngine.getHistoryService(); identityService = processEngine.getIdentityService(); managementService = processEngine.getManagementService(); + dynamicBpmnService = processEngine.getDynamicBpmnService(); } public void assertProcessEnded(final String processInstanceId) { diff --git a/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyServiceTask2.java b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyServiceTask2.java new file mode 100644 index 0000000000..6a6ebbcec9 --- /dev/null +++ b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyServiceTask2.java @@ -0,0 +1,31 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine.test.bpmn.servicetask; + +import org.activiti.engine.delegate.DelegateExecution; +import org.activiti.engine.delegate.JavaDelegate; + + +/** + * @author Tijs Rademakers + */ +public class DummyServiceTask2 implements JavaDelegate { + + public void execute(DelegateExecution execution) { + Integer count = (Integer) execution.getVariable("count2"); + count = count+1; + execution.setVariable("count2", count); + } + +} diff --git a/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyTestBean.java b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyTestBean.java new file mode 100644 index 0000000000..3f848c2379 --- /dev/null +++ b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyTestBean.java @@ -0,0 +1,27 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.activiti.engine.delegate.DelegateExecution; + +public class DummyTestBean implements Serializable { + + private static final long serialVersionUID = 1L; + + public void test(DelegateExecution execution) { + execution.setVariable("executed", true); + } +} diff --git a/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyTestDelegateBean.java b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyTestDelegateBean.java new file mode 100644 index 0000000000..77352bc9ab --- /dev/null +++ b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DummyTestDelegateBean.java @@ -0,0 +1,28 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.activiti.engine.delegate.DelegateExecution; +import org.activiti.engine.delegate.JavaDelegate; + +public class DummyTestDelegateBean implements JavaDelegate, Serializable { + + private static final long serialVersionUID = 1L; + + public void execute(DelegateExecution execution) { + execution.setVariable("executed", true); + } +} diff --git a/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.java b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.java new file mode 100644 index 0000000000..10ae98f838 --- /dev/null +++ b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.java @@ -0,0 +1,159 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine.test.bpmn.servicetask; + +import java.util.HashMap; +import java.util.Map; + +import org.activiti.engine.history.HistoricVariableInstance; +import org.activiti.engine.runtime.ProcessInstance; +import org.activiti.engine.task.Task; +import org.activiti.engine.test.Deployment; +import org.activiti5.engine.impl.test.PluggableActivitiTestCase; + +import com.fasterxml.jackson.databind.node.ObjectNode; + + +/** + * @author Tijs Rademakers + */ +public class DynamicServiceTaskTest extends PluggableActivitiTestCase { + + @Deployment + public void testChangeClassName() { + // first test without changing the class name + Map varMap = new HashMap(); + varMap.put("count", 0); + varMap.put("count2", 0); + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicServiceTask", varMap); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + assertEquals(1, runtimeService.getVariable(processInstance.getId(), "count")); + assertEquals(0, runtimeService.getVariable(processInstance.getId(), "count2")); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the class name + varMap = new HashMap(); + varMap.put("count", 0); + varMap.put("count2", 0); + processInstance = runtimeService.startProcessInstanceByKey("dynamicServiceTask", varMap); + + String processDefinitionId = processInstance.getProcessDefinitionId(); + ObjectNode infoNode = dynamicBpmnService.changeServiceTaskClassName("service", "org.activiti5.engine.test.bpmn.servicetask.DummyServiceTask2"); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + assertEquals(0, runtimeService.getVariable(processInstance.getId(), "count")); + assertEquals(1, runtimeService.getVariable(processInstance.getId(), "count2")); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment + public void testChangeExpression() { + // first test without changing the class name + DummyTestBean testBean = new DummyTestBean(); + Map varMap = new HashMap(); + varMap.put("bean", testBean); + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicServiceTask", varMap); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()) + .variableName("executed") + .singleResult(); + assertNotNull(historicVariableInstance); + assertTrue((Boolean) historicVariableInstance.getValue()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the class name + testBean = new DummyTestBean(); + varMap = new HashMap(); + varMap.put("bean2", testBean); + processInstance = runtimeService.startProcessInstanceByKey("dynamicServiceTask", varMap); + + String processDefinitionId = processInstance.getProcessDefinitionId(); + ObjectNode infoNode = dynamicBpmnService.changeServiceTaskExpression("service", "${bean2.test(execution)}"); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + historicVariableInstance = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()) + .variableName("executed") + .singleResult(); + assertNotNull(historicVariableInstance); + assertTrue((Boolean) historicVariableInstance.getValue()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment + public void testChangeDelegateExpression() { + // first test without changing the class name + DummyTestDelegateBean testBean = new DummyTestDelegateBean(); + Map varMap = new HashMap(); + varMap.put("bean", testBean); + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicServiceTask", varMap); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()) + .variableName("executed") + .singleResult(); + assertNotNull(historicVariableInstance); + assertTrue((Boolean) historicVariableInstance.getValue()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the class name + testBean = new DummyTestDelegateBean(); + varMap = new HashMap(); + varMap.put("bean2", testBean); + processInstance = runtimeService.startProcessInstanceByKey("dynamicServiceTask", varMap); + + String processDefinitionId = processInstance.getProcessDefinitionId(); + ObjectNode infoNode = dynamicBpmnService.changeServiceTaskDelegateExpression("service", "${bean2}"); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + historicVariableInstance = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()) + .variableName("executed") + .singleResult(); + assertNotNull(historicVariableInstance); + assertTrue((Boolean) historicVariableInstance.getValue()); + + assertProcessEnded(processInstance.getId()); + } +} \ No newline at end of file diff --git a/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.java b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.java new file mode 100644 index 0000000000..14ef86eca3 --- /dev/null +++ b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.java @@ -0,0 +1,115 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine.test.bpmn.usertask; + + + +import java.util.HashMap; +import java.util.Map; + +import org.activiti.engine.ProcessEngine; +import org.activiti.engine.ProcessEngineConfiguration; +import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.activiti.engine.runtime.ProcessInstance; +import org.activiti.engine.task.Task; +import org.activiti.engine.test.Deployment; +import org.activiti5.engine.impl.test.AbstractActivitiTestCase; + +import com.fasterxml.jackson.databind.node.ObjectNode; + + +/** + * @author Tijs Rademakers + */ +public class DisabledDefinitionInfoCacheTest extends AbstractActivitiTestCase { + + protected static ProcessEngine cachedProcessEngine; + + protected void initializeProcessEngine() { + if (cachedProcessEngine==null) { + ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) ProcessEngineConfiguration + .createProcessEngineConfigurationFromResource("org/activiti5/engine/test/bpmn/usertask/activiti.cfg.xml"); + + cachedProcessEngine = processEngineConfiguration.buildProcessEngine(); + } + processEngine = cachedProcessEngine; + } + + @Deployment + public void testChangeFormKey() { + // first test without changing the form key + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("test", task.getFormKey()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskFormKey("task1", "test2"); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("test", task.getFormKey()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment + public void testChangeClassName() { + // first test without changing the class name + Map varMap = new HashMap(); + varMap.put("count", 0); + varMap.put("count2", 0); + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicServiceTask", varMap); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + assertEquals(1, runtimeService.getVariable(processInstance.getId(), "count")); + assertEquals(0, runtimeService.getVariable(processInstance.getId(), "count2")); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the class name + varMap = new HashMap(); + varMap.put("count", 0); + varMap.put("count2", 0); + processInstance = runtimeService.startProcessInstanceByKey("dynamicServiceTask", varMap); + + String processDefinitionId = processInstance.getProcessDefinitionId(); + ObjectNode infoNode = dynamicBpmnService.changeServiceTaskClassName("service", "org.activiti5.engine.test.bpmn.servicetask.DummyServiceTask2"); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + assertEquals(1, runtimeService.getVariable(processInstance.getId(), "count")); + assertEquals(0, runtimeService.getVariable(processInstance.getId(), "count2")); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + +} diff --git a/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.java b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.java new file mode 100644 index 0000000000..9f1a4c795b --- /dev/null +++ b/modules/activiti5-test/src/test/java/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.java @@ -0,0 +1,417 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti5.engine.test.bpmn.usertask; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.activiti.engine.runtime.ProcessInstance; +import org.activiti.engine.task.IdentityLink; +import org.activiti.engine.task.IdentityLinkType; +import org.activiti.engine.task.Task; +import org.activiti.engine.test.Deployment; +import org.activiti5.engine.impl.test.PluggableActivitiTestCase; + +import com.fasterxml.jackson.databind.node.ObjectNode; + + +/** + * @author Tijs Rademakers + */ +public class DynamicUserTaskTest extends PluggableActivitiTestCase { + + @Deployment(resources={"org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.assignment.bpmn20.xml"}) + public void testChangeAssignee() { + // first test without changing the form key + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("test", task.getAssignee()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskAssignee("task1", "test2"); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("test2", task.getAssignee()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment(resources={"org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.assignment.bpmn20.xml"}) + public void testChangeOwner() { + // first test without changing the form key + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("ownerTest", task.getOwner()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskOwner("task1", "ownerTest2"); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("ownerTest2", task.getOwner()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment(resources={"org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.basictask.bpmn20.xml"}) + public void testChangeCandidateUsers() { + // first test without changing the form key + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + List taskIdentityLinks = taskService.getIdentityLinksForTask(task.getId()); + boolean candidateUserTestFound = false; + for (IdentityLink identityLink : taskIdentityLinks) { + if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getUserId() != null && identityLink.getGroupId() == null) { + if ("test".equals(identityLink.getUserId())) { + candidateUserTestFound = true; + } + } + } + assertFalse(candidateUserTestFound); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskCandidateUser("task1", "test", true); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskIdentityLinks = taskService.getIdentityLinksForTask(task.getId()); + candidateUserTestFound = false; + for (IdentityLink identityLink : taskIdentityLinks) { + if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getUserId() != null && identityLink.getGroupId() == null) { + if ("test".equals(identityLink.getUserId())) { + candidateUserTestFound = true; + } + } + } + assertTrue(candidateUserTestFound); + taskService.complete(task.getId()); + + infoNode = dynamicBpmnService.getProcessDefinitionInfo(processDefinitionId); + dynamicBpmnService.changeUserTaskCandidateUser("task1", "test2", false, infoNode); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskIdentityLinks = taskService.getIdentityLinksForTask(task.getId()); + candidateUserTestFound = false; + boolean candidateUserTest2Found = false; + for (IdentityLink identityLink : taskIdentityLinks) { + if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getUserId() != null && identityLink.getGroupId() == null) { + if ("test".equals(identityLink.getUserId())) { + candidateUserTestFound = true; + } else if ("test2".equals(identityLink.getUserId())) { + candidateUserTest2Found = true; + } + } + } + assertTrue(candidateUserTestFound); + assertTrue(candidateUserTest2Found); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment(resources={"org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.basictask.bpmn20.xml"}) + public void testChangeCandidateGroups() { + // first test without changing the form key + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + List taskIdentityLinks = taskService.getIdentityLinksForTask(task.getId()); + boolean candidateGroupTestFound = false; + for (IdentityLink identityLink : taskIdentityLinks) { + if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getGroupId() != null && identityLink.getUserId() == null) { + if ("test".equals(identityLink.getGroupId())) { + candidateGroupTestFound = true; + } + } + } + assertFalse(candidateGroupTestFound); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskCandidateGroup("task1", "test", true); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskIdentityLinks = taskService.getIdentityLinksForTask(task.getId()); + candidateGroupTestFound = false; + for (IdentityLink identityLink : taskIdentityLinks) { + if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getGroupId() != null && identityLink.getUserId() == null) { + if ("test".equals(identityLink.getGroupId())) { + candidateGroupTestFound = true; + } + } + } + assertTrue(candidateGroupTestFound); + taskService.complete(task.getId()); + + infoNode = dynamicBpmnService.getProcessDefinitionInfo(processDefinitionId); + dynamicBpmnService.changeUserTaskCandidateGroup("task1", "test2", false, infoNode); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskIdentityLinks = taskService.getIdentityLinksForTask(task.getId()); + candidateGroupTestFound = false; + boolean candidateGroupTest2Found = false; + for (IdentityLink identityLink : taskIdentityLinks) { + if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getGroupId() != null && identityLink.getUserId() == null) { + if ("test".equals(identityLink.getGroupId())) { + candidateGroupTestFound = true; + } else if ("test2".equals(identityLink.getGroupId())) { + candidateGroupTest2Found = true; + } + } + } + assertTrue(candidateGroupTestFound); + assertTrue(candidateGroupTest2Found); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment(resources={"org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.basictask.bpmn20.xml"}) + public void testChangeCandidateUsersAndGroups() { + // first test without changing the form key + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + List taskIdentityLinks = taskService.getIdentityLinksForTask(task.getId()); + boolean candidateUserTestFound = false; + boolean candidateGroupTestFound = false; + for (IdentityLink identityLink : taskIdentityLinks) { + if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getUserId() != null && identityLink.getGroupId() == null) { + if ("test".equals(identityLink.getUserId())) { + candidateUserTestFound = true; + } + } else if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getGroupId() != null && identityLink.getUserId() == null) { + if ("test".equals(identityLink.getGroupId())) { + candidateGroupTestFound = true; + } + } + } + assertFalse(candidateUserTestFound); + assertFalse(candidateGroupTestFound); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskCandidateGroup("task1", "test", true); + dynamicBpmnService.changeUserTaskCandidateUser("task1", "test", true, infoNode); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskIdentityLinks = taskService.getIdentityLinksForTask(task.getId()); + candidateUserTestFound = false; + candidateGroupTestFound = false; + for (IdentityLink identityLink : taskIdentityLinks) { + if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getUserId() != null && identityLink.getGroupId() == null) { + if ("test".equals(identityLink.getUserId())) { + candidateUserTestFound = true; + } + } else if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getGroupId() != null && identityLink.getUserId() == null) { + if ("test".equals(identityLink.getGroupId())) { + candidateGroupTestFound = true; + } + } + } + assertTrue(candidateUserTestFound); + assertTrue(candidateGroupTestFound); + taskService.complete(task.getId()); + + infoNode = dynamicBpmnService.getProcessDefinitionInfo(processDefinitionId); + dynamicBpmnService.changeUserTaskCandidateGroup("task1", "test2", false, infoNode); + dynamicBpmnService.changeUserTaskCandidateUser("task1", "test2", false, infoNode); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskIdentityLinks = taskService.getIdentityLinksForTask(task.getId()); + candidateUserTestFound = false; + boolean candidateUserTestFound2 = false; + candidateGroupTestFound = false; + boolean candidateGroupTest2Found = false; + for (IdentityLink identityLink : taskIdentityLinks) { + if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getUserId() != null && identityLink.getGroupId() == null) { + if ("test".equals(identityLink.getUserId())) { + candidateUserTestFound = true; + } else if ("test2".equals(identityLink.getUserId())) { + candidateUserTestFound2 = true; + } + } else if (IdentityLinkType.CANDIDATE.equals(identityLink.getType()) && identityLink.getGroupId() != null && identityLink.getUserId() == null) { + if ("test".equals(identityLink.getGroupId())) { + candidateGroupTestFound = true; + } else if ("test2".equals(identityLink.getGroupId())) { + candidateGroupTest2Found = true; + } + } + } + assertTrue(candidateUserTestFound); + assertTrue(candidateUserTestFound2); + assertTrue(candidateGroupTestFound); + assertTrue(candidateGroupTest2Found); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment(resources={"org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.basictask.bpmn20.xml"}) + public void testChangeNameAndDescription() { + // first test without changing the form key + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertNull(task.getName()); + assertNull(task.getDescription()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskName("task1", "Task name test"); + dynamicBpmnService.changeUserTaskDescription("task1", "Task description test", infoNode); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("Task name test", task.getName()); + assertEquals("Task description test", task.getDescription()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment(resources={"org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.assignment.bpmn20.xml"}) + public void testChangePriorityAndCategory() { + // first test without changing the form key + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals(50, task.getPriority()); + assertNull(task.getCategory()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskPriority("task1", "99"); + dynamicBpmnService.changeUserTaskCategory("task1", "categoryTest", infoNode); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals(99, task.getPriority()); + assertEquals("categoryTest", task.getCategory()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment + public void testChangeFormKey() { + // first test without changing the form key + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("test", task.getFormKey()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskFormKey("task1", "test2"); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask"); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("test2", task.getFormKey()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + + @Deployment + public void testChangeFormKeyWithExpression() { + // first test without changing the form key + Map varMap = new HashMap(); + varMap.put("start", "test"); + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask", varMap); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("test", task.getFormKey()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + + // now test with changing the form key + ObjectNode infoNode = dynamicBpmnService.changeUserTaskFormKey("task1", "${anotherKey}"); + dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); + + varMap = new HashMap(); + varMap.put("anotherKey", "test2"); + processInstance = runtimeService.startProcessInstanceByKey("dynamicUserTask", varMap); + + task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertEquals("test2", task.getFormKey()); + taskService.complete(task.getId()); + + assertProcessEnded(processInstance.getId()); + } + +} diff --git a/modules/activiti5-test/src/test/resources/activiti.cfg.xml b/modules/activiti5-test/src/test/resources/activiti.cfg.xml index 5986d7bc84..99ac32f4d9 100644 --- a/modules/activiti5-test/src/test/resources/activiti.cfg.xml +++ b/modules/activiti5-test/src/test/resources/activiti.cfg.xml @@ -45,6 +45,8 @@ + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeClassName.bpmn20.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeClassName.bpmn20.xml new file mode 100644 index 0000000000..a6da2da01f --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeClassName.bpmn20.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeDelegateExpression.bpmn20.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeDelegateExpression.bpmn20.xml new file mode 100644 index 0000000000..0cb323a007 --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeDelegateExpression.bpmn20.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeExpression.bpmn20.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeExpression.bpmn20.xml new file mode 100644 index 0000000000..a0a4179b70 --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/servicetask/DynamicServiceTaskTest.testChangeExpression.bpmn20.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.testChangeClassName.bpmn20.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.testChangeClassName.bpmn20.xml new file mode 100644 index 0000000000..a6da2da01f --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.testChangeClassName.bpmn20.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.testChangeFormKey.bpmn20.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.testChangeFormKey.bpmn20.xml new file mode 100644 index 0000000000..d807870121 --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DisabledDefinitionInfoCacheTest.testChangeFormKey.bpmn20.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.assignment.bpmn20.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.assignment.bpmn20.xml new file mode 100644 index 0000000000..d12f7cb1ba --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.assignment.bpmn20.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.basictask.bpmn20.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.basictask.bpmn20.xml new file mode 100644 index 0000000000..3796a62ef6 --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.basictask.bpmn20.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.testChangeFormKey.bpmn20.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.testChangeFormKey.bpmn20.xml new file mode 100644 index 0000000000..d807870121 --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.testChangeFormKey.bpmn20.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.testChangeFormKeyWithExpression.bpmn20.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.testChangeFormKeyWithExpression.bpmn20.xml new file mode 100644 index 0000000000..4f8e72b454 --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/DynamicUserTaskTest.testChangeFormKeyWithExpression.bpmn20.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/activiti.cfg.xml b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/activiti.cfg.xml new file mode 100644 index 0000000000..e56aa2ecad --- /dev/null +++ b/modules/activiti5-test/src/test/resources/org/activiti5/engine/test/bpmn/usertask/activiti.cfg.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + -- GitLab