Skip to content

Commit

Permalink
chore(merge): release-10.2.0 into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
bonita-ci committed Aug 23, 2024
2 parents 1c8afca + ad268f8 commit 7bd6a26
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;
import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;
Expand All @@ -26,6 +27,7 @@
import org.bonitasoft.engine.bpm.process.ProcessInstance;
import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;
import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;
import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;
import org.bonitasoft.engine.event.AbstractEventIT;
import org.bonitasoft.engine.exception.BonitaRuntimeException;
import org.bonitasoft.engine.expression.Expression;
Expand Down Expand Up @@ -336,4 +338,41 @@ private ProcessDefinition deployAndEnableProcessWithCallActivity(final String pr
return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user);
}

@Test
public void timerBoundaryEvent_should_not_trigger_and_be_deleted_at_flownode_abortion() throws Exception {
final int timerDuration = 20_000;//long enough not to trigger
SchedulerService schedulerService = getServiceAccessor().getSchedulerService();

final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()
.createNewInstance("pTimerBoundary", "2.0");
processDefinitionBuilder.addActor(ACTOR_NAME);
processDefinitionBuilder.addStartEvent("start");
final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask("step1",
ACTOR_NAME);
userTaskDefinitionBuilder.addBoundaryEvent("Boundary timer").addTimerEventTriggerDefinition(TimerType.DURATION,
new ExpressionBuilder().createConstantLongExpression(timerDuration));
userTaskDefinitionBuilder.addUserTask("exceptionStep", ACTOR_NAME);
processDefinitionBuilder.addUserTask("step2", ACTOR_NAME);
processDefinitionBuilder.addEndEvent("end").addTerminateEventTrigger();
processDefinitionBuilder.addEndEvent("end2").addTerminateEventTrigger();
processDefinitionBuilder.addTransition("start", "step1");
processDefinitionBuilder.addTransition("start", "step2");
processDefinitionBuilder.addTransition("step1", "end");
processDefinitionBuilder.addTransition("step2", "end2");
processDefinitionBuilder.addTransition("Boundary timer", "exceptionStep");
processDefinitionBuilder.addTransition("exceptionStep", "end");

final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),
ACTOR_NAME, user);

final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());

waitForUserTask(processInstance.getId(), "step1");
waitForUserTaskAssignAndExecuteIt(processInstance, "step2", user, Map.of());
waitForProcessToFinish(processInstance);
List<String> allJobs = schedulerService.getAllJobs();
assertThat(allJobs).isEmpty();
disableAndDeleteProcess(processDefinition);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
**/
package org.bonitasoft.engine.test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;

import java.io.File;
Expand All @@ -37,11 +36,14 @@
import org.bonitasoft.engine.bpm.bar.BusinessArchive;
import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;
import org.bonitasoft.engine.bpm.connector.ConnectorEvent;
import org.bonitasoft.engine.bpm.flownode.*;
import org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;
import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;
import org.bonitasoft.engine.bpm.flownode.ActivityStates;
import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;
import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;
import org.bonitasoft.engine.bpm.process.ProcessDefinition;
import org.bonitasoft.engine.bpm.process.ProcessInstance;
import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;
import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;
import org.bonitasoft.engine.connectors.VariableStorage;
import org.bonitasoft.engine.core.process.comment.api.SCommentService;
import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;
Expand All @@ -57,7 +59,6 @@
import org.bonitasoft.engine.persistence.OrderByType;
import org.bonitasoft.engine.persistence.QueryOptions;
import org.bonitasoft.engine.platform.Platform;
import org.bonitasoft.engine.scheduler.SchedulerService;
import org.bonitasoft.engine.search.SearchOptionsBuilder;
import org.bonitasoft.engine.search.SearchResult;
import org.bonitasoft.engine.service.ServiceAccessor;
Expand Down Expand Up @@ -547,41 +548,4 @@ public void should_warn_when_setting_remote_connection_with_local_engine() throw
Assertions.assertThat(log).contains(
"You are declaring an API access to Bonita Engine as a remote connection, whereas it looks like you are running in the same JVM.");
}

@Test
public void timerBoundaryEvent_should_not_trigger_and_be_deleted_at_flownode_execution() throws Exception {
final int timerDuration = 10000;//long enough not to trigger
SchedulerService schedulerService = getServiceAccessor().getSchedulerService();

final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()
.createNewInstance("pTimerBoundary", "2.0");
processDefinitionBuilder.addActor(ACTOR_NAME);
processDefinitionBuilder.addStartEvent("start");
final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask("step1",
ACTOR_NAME);
userTaskDefinitionBuilder.addBoundaryEvent("Boundary timer").addTimerEventTriggerDefinition(TimerType.DURATION,
new ExpressionBuilder().createConstantLongExpression(timerDuration));
userTaskDefinitionBuilder.addUserTask("exceptionStep", ACTOR_NAME);
userTaskDefinitionBuilder.addUserTask("step2", ACTOR_NAME);
processDefinitionBuilder.addEndEvent("end");
processDefinitionBuilder.addTransition("start", "step1");
processDefinitionBuilder.addTransition("step1", "step2");
processDefinitionBuilder.addTransition("Boundary timer", "exceptionStep");
processDefinitionBuilder.addTransition("exceptionStep", "end");

final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),
ACTOR_NAME, john);

final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());

waitForUserTaskAndExecuteIt(processInstance, "step1", john);
Thread.sleep(1000);//The work needs some time to finish
List<String> allJobs = schedulerService.getAllJobs();
assertThat(allJobs).isEmpty();
waitForUserTaskAndExecuteIt(processInstance, "step2", john);
waitForProcessToFinish(processInstance);

disableAndDeleteProcess(processDefinition);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;
import static org.bonitasoft.engine.core.process.instance.model.SStateCategory.ABORTING;

import java.util.Collections;
import java.util.List;

import org.bonitasoft.engine.SArchivingException;
import org.bonitasoft.engine.bpm.process.ProcessInstanceState;
import org.bonitasoft.engine.builder.BuilderFactory;
Expand All @@ -30,32 +27,25 @@
import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;
import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;
import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition;
import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;
import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException;
import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;
import org.bonitasoft.engine.core.process.instance.api.states.StateCode;
import org.bonitasoft.engine.core.process.instance.model.SActivityInstance;
import org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;
import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;
import org.bonitasoft.engine.core.process.instance.model.SProcessInstance;
import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;
import org.bonitasoft.engine.dependency.model.ScopeType;
import org.bonitasoft.engine.execution.archive.BPMArchiverService;
import org.bonitasoft.engine.execution.job.JobNameBuilder;
import org.bonitasoft.engine.execution.state.FlowNodeStateManager;
import org.bonitasoft.engine.execution.work.BPMWorkFactory;
import org.bonitasoft.engine.persistence.*;
import org.bonitasoft.engine.scheduler.SchedulerService;
import org.bonitasoft.engine.persistence.SBonitaReadException;
import org.bonitasoft.engine.work.SWorkRegisterException;
import org.bonitasoft.engine.work.WorkService;
import org.slf4j.Logger;
Expand Down Expand Up @@ -85,8 +75,6 @@ public class FlowNodeExecutorImpl implements FlowNodeExecutor {
private final BPMWorkFactory workFactory;
private final ProcessInstanceInterruptor processInstanceInterruptor;
private final BPMArchiverService bpmArchiverService;
private final EventInstanceService eventInstanceService;
private final SchedulerService schedulerService;

public FlowNodeExecutorImpl(final FlowNodeStateManager flowNodeStateManager,
final ActivityInstanceService activityInstanceManager,
Expand All @@ -96,8 +84,7 @@ public FlowNodeExecutorImpl(final FlowNodeStateManager flowNodeStateManager,
final ClassLoaderService classLoaderService,
final WorkService workService, BPMWorkFactory workFactory,
final ProcessInstanceInterruptor processInstanceInterruptor,
final BPMArchiverService bpmArchiverService, EventInstanceService eventInstanceService,
SchedulerService schedulerService) {
final BPMArchiverService bpmArchiverService) {
this.flowNodeStateManager = flowNodeStateManager;
activityInstanceService = activityInstanceManager;
this.containerRegistry = containerRegistry;
Expand All @@ -109,8 +96,6 @@ public FlowNodeExecutorImpl(final FlowNodeStateManager flowNodeStateManager,
this.processDefinitionService = processDefinitionService;
this.commentService = commentService;
this.bpmArchiverService = bpmArchiverService;
this.eventInstanceService = eventInstanceService;
this.schedulerService = schedulerService;

}

Expand Down Expand Up @@ -297,7 +282,6 @@ public void childFinished(final long processDefinitionId, final long parentId, S
final FlowNodeState state = flowNodeStateManager.getState(activityInstanceParent.getStateId());
final boolean shouldContinueParent = state.notifyChildFlowNodeHasFinished(sProcessDefinition,
activityInstanceParent, childFlowNode);
deleteTimerJobsOnFlownode(childFlowNode);
if (shouldContinueParent) {
// it should never happen, because the terminal state never waits children to finish
if (activityInstanceParent.isTerminal()) {
Expand All @@ -311,58 +295,6 @@ public void childFinished(final long processDefinitionId, final long parentId, S
}
}

private void deleteTimerJobsOnFlownode(SFlowNodeInstance flowNodeInstance)
throws SProcessDefinitionNotFoundException, SBonitaReadException {
// We have no way of going from flownodeInst -> boundary timer of flownode, so we have to do a search
SProcessDefinition processDefinition = processDefinitionService
.getProcessDefinition(flowNodeInstance.getProcessDefinitionId());
List<SBoundaryEventDefinition> processBoundaryEventDefinitions = processDefinition.getProcessContainer()
.getBoundaryEvents();
for (SBoundaryEventDefinition sBoundaryEventDefinition : processBoundaryEventDefinitions) {
final List<OrderByOption> orderByOptions = Collections
.singletonList(new OrderByOption(SCatchEventInstance.class, "id", OrderByType.ASC));
final FilterOption filterOption = new FilterOption(SCatchEventInstance.class, "activityInstanceId",
flowNodeInstance.getId());
QueryOptions queryOptions = new QueryOptions(0, 100, orderByOptions,
Collections.singletonList(filterOption), null);
List<SCatchEventInstance> sBoundaryEventInstances = activityInstanceService
.searchFlowNodeInstances(SCatchEventInstance.class, queryOptions);
for (SCatchEventInstance sCatchEventInstance : sBoundaryEventInstances) {
deleteJobsOnFlowNodeInstance(processDefinition, sBoundaryEventDefinition, sCatchEventInstance);
}
}
}

private void deleteJobsOnFlowNodeInstance(final SProcessDefinition processDefinition,
final SCatchEventDefinition sCatchEventDefinition,
final SCatchEventInstance sCatchEventInstance) {
try {
if (!sCatchEventDefinition.getTimerEventTriggerDefinitions().isEmpty()) {
final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(),
sCatchEventDefinition, sCatchEventInstance);
final boolean delete = schedulerService.delete(jobName);
try {
eventInstanceService.deleteEventTriggerInstanceOfFlowNode(sCatchEventInstance.getId());
} catch (SEventTriggerInstanceDeletionException e) {
LOG.warn(
"Unable to delete event trigger of flow node instance {}: {}", sCatchEventInstance,
e.getMessage());
}
if (!delete && schedulerService.isExistingJob(jobName)) {
LOG.warn(
"No job found with name '{}' when interrupting timer catch event named '{}' and id '{}'. It was probably already triggered.",
jobName,
sCatchEventDefinition.getName(),
sCatchEventInstance.getId());

}
}
} catch (final Exception e) {
LOG.error("An error occurred while deleting jobs attached to catch event instance '{}'.",
sCatchEventDefinition, e);
}
}

private void decrementToken(final SActivityInstance sActivityInstance) throws SFlowNodeModificationException {
final int tokenCount = sActivityInstance.getTokenCount() - 1;
activityInstanceService.setTokenCount(sActivityInstance, tokenCount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import org.springframework.stereotype.Component;

@Component
public class AbortingBoundaryAndIntermediateCatchEventState extends EndingIntermediateCatchEventExceptionState {
public class AbortingBoundaryAndIntermediateCatchEventState extends InterruptingBoundaryAndIntermediateCatchEventState {

public AbortingBoundaryAndIntermediateCatchEventState(WaitingEventsInterrupter waitingEventsInterrupter) {
super(waitingEventsInterrupter);
Expand Down Expand Up @@ -47,7 +47,7 @@ public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {

@Override
public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {
return "Aborting intermediate catch event";
return "Aborting boundary or intermediate catch event";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

@Component
public class CancellingBoundaryAndIntermediateCatchEventState
extends EndingIntermediateCatchEventExceptionState {
extends InterruptingBoundaryAndIntermediateCatchEventState {

public CancellingBoundaryAndIntermediateCatchEventState(WaitingEventsInterrupter waitingEventsInterrupter) {
super(waitingEventsInterrupter);
Expand Down Expand Up @@ -48,7 +48,7 @@ public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {

@Override
public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {
return "Canceling intermediate catch event";
return "Canceling boundary or intermediate catch event";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;
import org.bonitasoft.engine.core.process.instance.api.states.StateCode;
import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;
import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;
import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;
import org.bonitasoft.engine.execution.WaitingEventsInterrupter;

public abstract class EndingIntermediateCatchEventExceptionState implements FlowNodeState {
public abstract class InterruptingBoundaryAndIntermediateCatchEventState implements FlowNodeState {

private final WaitingEventsInterrupter waitingEventsInterrupter;

public EndingIntermediateCatchEventExceptionState(WaitingEventsInterrupter waitingEventsInterrupter) {
public InterruptingBoundaryAndIntermediateCatchEventState(WaitingEventsInterrupter waitingEventsInterrupter) {
super();
this.waitingEventsInterrupter = waitingEventsInterrupter;
}
Expand All @@ -45,8 +45,8 @@ public StateCode execute(final SProcessDefinition processDefinition, final SFlow
.getFlowNode(
instance.getFlowNodeDefinitionId());
try {
final SIntermediateCatchEventInstance intermediateCatchEventInstance = (SIntermediateCatchEventInstance) instance;
waitingEventsInterrupter.interruptWaitingEvents(processDefinition, intermediateCatchEventInstance,
final SCatchEventInstance catchEventInstance = (SCatchEventInstance) instance;
waitingEventsInterrupter.interruptWaitingEvents(processDefinition, catchEventInstance,
catchEventDef);
} catch (final SBonitaException e) {
throw new SActivityStateExecutionException(e);
Expand Down
Loading

0 comments on commit 7bd6a26

Please sign in to comment.