Skip to content

Commit

Permalink
Fix: delete related historic case/process instance to process/case in…
Browse files Browse the repository at this point in the history
…stance using case / process task
  • Loading branch information
jbarrez committed Jul 19, 2023
1 parent 3f5005e commit 9f265ea
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,19 @@ public void deleteCaseInstancesForExecutionId(String executionId) {
}

@Override
public void deleteCaseInstanceWithoutAgenda(String caseInstanceId) {
public void deleteCaseInstanceWithoutAgenda(String caseInstanceId, boolean deleteInHistory) {
cmmnEngineConfiguration.getCommandExecutor().execute(commandContext -> {
CaseInstanceEntity caseInstanceEntity = CommandContextUtil.getCaseInstanceEntityManager(commandContext).findById(caseInstanceId);
if (caseInstanceEntity == null || caseInstanceEntity.isDeleted()) {
return null;
}

cmmnEngineConfiguration.getCmmnHistoryManager().recordCaseInstanceEnd(
caseInstanceEntity, CaseInstanceState.TERMINATED, cmmnEngineConfiguration.getClock().getCurrentTime());
if (deleteInHistory) {
cmmnEngineConfiguration.getCmmnHistoryManager().recordHistoricCaseInstanceDeleted(caseInstanceId, caseInstanceEntity.getTenantId());
} else {
cmmnEngineConfiguration.getCmmnHistoryManager().recordCaseInstanceEnd(
caseInstanceEntity, CaseInstanceState.TERMINATED, cmmnEngineConfiguration.getClock().getCurrentTime());
}

cmmnEngineConfiguration.getCaseInstanceEntityManager().delete(caseInstanceEntity.getId(), false, "cmmn-state-transition-delete-case");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,15 +148,15 @@ public List<IOParameter> getOutputParametersOfCaseTask(String executionId) {
}

@Override
public void deleteProcessInstance(String processInstanceId) {
public void deleteProcessInstance(String processInstanceId, boolean cascade) {
processEngineConfiguration.getCommandExecutor().execute(commandContext -> {

ExecutionEntity processInstanceEntity = CommandContextUtil.getExecutionEntityManager(commandContext).findById(processInstanceId);
if (processInstanceEntity == null || processInstanceEntity.isDeleted()) {
return null;
}

CommandContextUtil.getExecutionEntityManager(commandContext).deleteProcessInstance(processInstanceEntity.getProcessInstanceId(), DELETE_REASON, false);
CommandContextUtil.getExecutionEntityManager(commandContext).deleteProcessInstance(processInstanceEntity.getProcessInstanceId(), DELETE_REASON, cascade);

return null;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,80 @@ public void testParallelMultiInstanceCaseTask() {
}
}

@Test
public void testProcessDeploymentDeleteDeletesRelatedCaseInstances() {
Deployment deployment = processEngineRepositoryService.createDeployment()
.addClasspathResource("org/flowable/cmmn/test/caseTaskProcess.bpmn20.xml")
.deploy();

org.flowable.cmmn.api.repository.CmmnDeployment cmmnDeployment = cmmnRepositoryService.createDeployment()
.addClasspathResource("org/flowable/cmmn/test/CaseTaskTest.testCaseTask.cmmn")
.deploy();

// Start process instance, complete a user task to arrive at the case task
ProcessInstance processInstance = processEngineRuntimeService.startProcessInstanceByKey("caseTask");
processEngineTaskService.complete(processEngineTaskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId());
assertThat(processEngineRuntimeService.createProcessInstanceQuery().count()).isOne();
assertThat(cmmnRuntimeService.createCaseInstanceQuery().count()).isOne();

CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceQuery().singleResult();
assertThat(caseInstance).isNotNull();

if (HistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.AUDIT, (ProcessEngineConfigurationImpl) processEngineConfiguration)) {
assertThat(processEngineHistoryService.createHistoricProcessInstanceQuery().count()).isOne();
assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().count()).isOne();
}

// Deleting the process deployment also should delete the case instance
processEngineRepositoryService.deleteDeployment(deployment.getId(), true);

assertThat(processEngineRuntimeService.createProcessInstanceQuery().count()).isZero();
assertThat(cmmnRuntimeService.createCaseInstanceQuery().count()).isZero();

if (HistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.AUDIT, (ProcessEngineConfigurationImpl) processEngineConfiguration)) {
assertThat(processEngineHistoryService.createHistoricProcessInstanceQuery().count()).isZero();
assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().count()).isZero();
}
}

// TODO: this needs a DB schema change (adding process instance id to historic case instance)
// @Test
// public void testCompletedProcessDeploymentDeleteDeletesRelatedCaseInstances() {
// // Same test as testProcessDeploymentDeleteDeletesRelatedCaseInstances, but now the process instance and case instance are completed
//
// Deployment deployment = processEngineRepositoryService.createDeployment()
// .addClasspathResource("org/flowable/cmmn/test/caseTaskProcess.bpmn20.xml")
// .deploy();
//
// org.flowable.cmmn.api.repository.CmmnDeployment cmmnDeployment = cmmnRepositoryService.createDeployment()
// .addClasspathResource("org/flowable/cmmn/test/CaseTaskTest.testCaseTask.cmmn")
// .deploy();
//
// ProcessInstance processInstance = processEngineRuntimeService.startProcessInstanceByKey("caseTask");
// processEngineTaskService.complete(processEngineTaskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId());
// cmmnTaskService.complete(cmmnTaskService.createTaskQuery().singleResult().getId());
// processEngineTaskService.complete(processEngineTaskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId());
//
// if (HistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.AUDIT, (ProcessEngineConfigurationImpl) processEngineConfiguration)) {
// assertThat(processEngineHistoryService.createHistoricProcessInstanceQuery().count()).isOne();
// assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().count()).isOne();
// }
//
// assertThat(processEngineRuntimeService.createProcessInstanceQuery().count()).isZero();
// assertThat(cmmnRuntimeService.createCaseInstanceQuery().count()).isZero();
//
// // Deleting the process deployment also should delete the case instance
// processEngineRepositoryService.deleteDeployment(deployment.getId(), true);
//
// assertThat(processEngineRuntimeService.createProcessInstanceQuery().count()).isZero();
// assertThat(cmmnRuntimeService.createCaseInstanceQuery().count()).isZero();
//
// if (HistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.AUDIT, (ProcessEngineConfigurationImpl) processEngineConfiguration)) {
// assertThat(processEngineHistoryService.createHistoricProcessInstanceQuery().count()).isZero();
// assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().count()).isZero();
// }
// }


static class ClearExecutionReferenceCmd implements Command<Void> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2035,4 +2035,33 @@ public void testDeleteCaseInstanceWithRepeatingProcessTask() {
assertThat(cmmnTaskService.createTaskQuery().count()).isZero();
}

@Test
public void testCaseDeploymentDeleteDeletesRelatedProcessInstances() {
org.flowable.cmmn.api.repository.CmmnDeployment cmmnDeployment = cmmnRepositoryService.createDeployment()
.addClasspathResource("org/flowable/cmmn/test/ProcessTaskTest.testOneTaskProcessBlocking.cmmn")
.deploy();

// Start case instance, complete a user task to arrive at the case task
startCaseInstanceWithOneTaskProcess();

ProcessInstance processInstance = processEngineRuntimeService.createProcessInstanceQuery().singleResult();
assertThat(processInstance).isNotNull();

if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) {
assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().count()).isOne();
assertThat(processEngineHistoryService.createHistoricProcessInstanceQuery().count()).isOne();
}

// Deleting the case deployment also should delete the process instance
cmmnRepositoryService.deleteDeployment(cmmnDeployment.getId(), true);

assertThat(processEngineRuntimeService.createProcessInstanceQuery().count()).isZero();
assertThat(cmmnRuntimeService.createCaseInstanceQuery().count()).isZero();

if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) {
assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().count()).isZero();
assertThat(processEngineHistoryService.createHistoricProcessInstanceQuery().count()).isZero();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -157,30 +157,30 @@ public void trigger(CommandContext commandContext, PlanItemInstanceEntity planIt

// Triggering the plan item (as opposed to a regular complete) terminates the process instance
CommandContextUtil.getAgenda(commandContext).planCompletePlanItemInstanceOperation(planItemInstance);
deleteProcessInstance(commandContext, planItemInstance);
deleteProcessInstance(commandContext, planItemInstance, false);
}

@Override
public void onStateTransition(CommandContext commandContext, DelegatePlanItemInstance planItemInstance, String transition) {
if (PlanItemInstanceState.ACTIVE.equals(planItemInstance.getState())) {
// The process task plan item will be deleted by the regular TerminatePlanItemOperation
if (PlanItemTransition.TERMINATE.equals(transition) || PlanItemTransition.EXIT.equals(transition)) {
deleteProcessInstance(commandContext, planItemInstance);
deleteProcessInstance(commandContext, planItemInstance, false);

}
}
}

protected void deleteProcessInstance(CommandContext commandContext, DelegatePlanItemInstance planItemInstance) {
protected void deleteProcessInstance(CommandContext commandContext, DelegatePlanItemInstance planItemInstance, boolean cascade) {
ProcessInstanceService processInstanceService = CommandContextUtil.getCmmnEngineConfiguration(commandContext).getProcessInstanceService();
processInstanceService.deleteProcessInstance(planItemInstance.getReferenceId());
processInstanceService.deleteProcessInstance(planItemInstance.getReferenceId(), cascade);
}

@Override
public void deleteChildEntity(CommandContext commandContext, DelegatePlanItemInstance delegatePlanItemInstance, boolean cascade) {
if (ReferenceTypes.PLAN_ITEM_CHILD_PROCESS.equals(delegatePlanItemInstance.getReferenceType())) {
delegatePlanItemInstance.setState(PlanItemInstanceState.TERMINATED); // This is not the regular termination, but the state still needs to be correct
deleteProcessInstance(commandContext, delegatePlanItemInstance);
deleteProcessInstance(commandContext, delegatePlanItemInstance, cascade);
} else {
throw new FlowableException("Can only delete a child entity for a plan item with reference type " + ReferenceTypes.PLAN_ITEM_CHILD_PROCESS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ String startProcessInstanceByKey(String processDefinitionKey, String predefinedP
/**
* Deletes the given process instance. Typically used to propagate termination.
*/
void deleteProcessInstance(String processInstanceId);
void deleteProcessInstance(String processInstanceId, boolean cascade);

/**
* Returns the variable value for a given variable.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,7 @@ protected void terminatePlanItemInstance(PlanItemInstanceEntity planItemInstance

} else if (planItemDefinition instanceof ProcessTask) {
if (planItemInstance.getReferenceId() != null) {
cmmnEngineConfiguration.getProcessInstanceService().deleteProcessInstance(planItemInstance.getReferenceId());
cmmnEngineConfiguration.getProcessInstanceService().deleteProcessInstance(planItemInstance.getReferenceId(), false);
}

} else if (planItemDefinition instanceof EventListener) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ protected void createChildExecutionForSubProcess(SubProcess subProcess) {
subProcessExecution.setCurrentFlowElement(subProcess);
subProcessExecution.setScope(true);

CommandContextUtil.getExecutionEntityManager(commandContext).deleteRelatedDataForExecution(execution, null, false);
CommandContextUtil.getExecutionEntityManager(commandContext).deleteRelatedDataForExecution(execution, null, false, false);
CommandContextUtil.getExecutionEntityManager(commandContext).delete(execution);
execution = subProcessExecution;
}
Expand Down Expand Up @@ -253,7 +253,7 @@ protected ExecutionEntity createMultiInstanceRootExecution(ExecutionEntity execu
FlowElement flowElement = execution.getCurrentFlowElement();

ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
executionEntityManager.deleteRelatedDataForExecution(execution, null, false);
executionEntityManager.deleteRelatedDataForExecution(execution, null, false, false);
executionEntityManager.delete(execution);

ExecutionEntity multiInstanceRootExecution = executionEntityManager.createChildExecution(parentExecution);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ protected void cleanupMiRoot(DelegateExecution execution) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
Collection<String> executionIdsNotToSendCancelledEventsFor = execution.isMultiInstanceRoot() ? null : Collections.singletonList(execution.getId());
executionEntityManager.deleteChildExecutions(multiInstanceRootExecution, null, executionIdsNotToSendCancelledEventsFor, DELETE_REASON_END, true, flowElement);
executionEntityManager.deleteRelatedDataForExecution(multiInstanceRootExecution, DELETE_REASON_END, false);
executionEntityManager.deleteRelatedDataForExecution(multiInstanceRootExecution, DELETE_REASON_END, false, false);
executionEntityManager.delete(multiInstanceRootExecution);

ExecutionEntity newExecution = executionEntityManager.createChildExecution(parentExecution);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public void execute(DelegateExecution execution) {

// The current execution will be reused and not deleted
if (!joinedExecution.getId().equals(execution.getId())) {
executionEntityManager.deleteRelatedDataForExecution(joinedExecution, null, false);
executionEntityManager.deleteRelatedDataForExecution(joinedExecution, null, false, false);
executionEntityManager.delete(joinedExecution);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ String startCaseInstanceByKey(String caseDefinitionKey, String predefinedCaseIns

void deleteCaseInstancesForExecutionId(String executionId);

void deleteCaseInstanceWithoutAgenda(String caseInstanceId);
void deleteCaseInstanceWithoutAgenda(String caseInstanceId, boolean deleteInHistory);

}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ void deleteExecutionAndRelatedData(ExecutionEntity executionEntity, String delet

void deleteExecutionAndRelatedData(ExecutionEntity executionEntity, String deleteReason, boolean deleteHistory);

void deleteRelatedDataForExecution(ExecutionEntity executionEntity, String deleteReason, boolean directDeleteInDatabase);
void deleteRelatedDataForExecution(ExecutionEntity executionEntity, String deleteReason, boolean deleteHistory, boolean directDeleteInDatabase);

void updateProcessInstanceLockTime(String processInstanceId, String lockOwner, Date lockTime);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ public void deleteExecutionAndRelatedData(ExecutionEntity executionEntity, Strin
CommandContextUtil.getActivityInstanceEntityManager().recordActivityEnd(executionEntity, deleteReason);
}

deleteRelatedDataForExecution(executionEntity, deleteReason, directDeleteInDatabase);
deleteRelatedDataForExecution(executionEntity, deleteReason, deleteHistory, directDeleteInDatabase);
delete(executionEntity);

if (cancel && !executionEntity.isProcessInstanceType()) {
Expand Down Expand Up @@ -817,10 +817,8 @@ public ExecutionEntity findFirstMultiInstanceRoot(ExecutionEntity executionEntit
return null;
}

protected CachedEntityMatcher<IdentityLinkEntity> identityLinkByProcessInstanceMatcher = new IdentityLinksByProcessInstanceMatcher();

@Override
public void deleteRelatedDataForExecution(ExecutionEntity executionEntity, String deleteReason, boolean directDeleteInDatabase) {
public void deleteRelatedDataForExecution(ExecutionEntity executionEntity, String deleteReason, boolean deleteHistory, boolean directDeleteInDatabase) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ending and deleting execution {} Reason: {}", executionEntity, deleteReason);
}
Expand All @@ -843,16 +841,16 @@ public void deleteRelatedDataForExecution(ExecutionEntity executionEntity, Strin
deleteJobs(executionEntity, commandContext, enableExecutionRelationshipCounts, eventDispatcherEnabled);
deleteEventSubScriptions(executionEntity, enableExecutionRelationshipCounts, eventDispatcherEnabled);
deleteActivityInstances(executionEntity, commandContext);
deleteSubCases(executionEntity, directDeleteInDatabase, commandContext);
deleteSubCases(executionEntity, deleteHistory, directDeleteInDatabase, commandContext);
}

protected void deleteSubCases(ExecutionEntity executionEntity, boolean directDeleteInDatabase, CommandContext commandContext) {
protected void deleteSubCases(ExecutionEntity executionEntity, boolean deleteHistory, boolean directDeleteInDatabase, CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
CaseInstanceService caseInstanceService = processEngineConfiguration.getCaseInstanceService();
if (caseInstanceService != null) {
if (executionEntity.getReferenceId() != null && ReferenceTypes.EXECUTION_CHILD_CASE.equals(executionEntity.getReferenceType())) {
if (directDeleteInDatabase) {
caseInstanceService.deleteCaseInstanceWithoutAgenda(executionEntity.getReferenceId());
caseInstanceService.deleteCaseInstanceWithoutAgenda(executionEntity.getReferenceId(), deleteHistory);
} else {
caseInstanceService.deleteCaseInstance(executionEntity.getReferenceId());
}
Expand Down

0 comments on commit 9f265ea

Please sign in to comment.