diff --git a/bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/failure/FlowNodeFailureIT.java b/bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/failure/FlowNodeFailureIT.java index a84fd86350c..18ece01d495 100644 --- a/bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/failure/FlowNodeFailureIT.java +++ b/bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/failure/FlowNodeFailureIT.java @@ -24,6 +24,7 @@ import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; +import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.OperatorType; @@ -73,8 +74,13 @@ public void create_a_failure_on_flownode_operation_exception() throws Exception var failures = serviceAccessor.getTransactionService() .executeInTransaction(() -> failureService.getFlowNodeFailures(failFlowNodeInstance.getId(), 5)); assertThat(failures).hasSize(1); - assertThat(failures.get(0).getErrorMessage()) - .isEqualTo("java.lang.RuntimeException: Failed !"); + var failure = failures.get(0); + assertThat(failure.getScope()) + .isEqualTo(ScopedException.OPERATION); + assertThat(failure.getContext()) + .isEqualTo("expression::my-failing-script"); + assertThat(failure.getErrorMessage()) + .isEqualTo("RuntimeException: Failed !"); disableAndDeleteProcess(processDefinition); } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/model/impl/BPMInstancesCreator.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/model/impl/BPMInstancesCreator.java index 99affbba0c6..fb29095f2ad 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/model/impl/BPMInstancesCreator.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/model/impl/BPMInstancesCreator.java @@ -27,6 +27,7 @@ import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.data.instance.TransientDataService; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; @@ -762,7 +763,7 @@ void warningWhenTransientDataWithNullValue() { } private void createDataForProcess(final List sDataInstances) - throws SDataInstanceException, SFlowNodeNotFoundException, SFlowNodeReadException { + throws SDataInstanceException { if (!sDataInstances.isEmpty()) { for (final SDataInstance sDataInstance : sDataInstances) { dataInstanceService.createDataInstance(sDataInstance); @@ -874,25 +875,33 @@ private boolean createDataInstances(final SActivityDefinition activityDefinition throws SActivityStateExecutionException { final List sDataDefinitions = activityDefinition.getSDataDefinitions(); final SLoopCharacteristics loopCharacteristics = activityDefinition.getLoopCharacteristics(); - try { - if (loopCharacteristics instanceof SMultiInstanceLoopCharacteristics - && (((SMultiInstanceLoopCharacteristics) loopCharacteristics).getDataInputItemRef() != null - || ((SMultiInstanceLoopCharacteristics) loopCharacteristics) - .getDataOutputItemRef() != null)) { + + if (loopCharacteristics instanceof SMultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics + && (multiInstanceLoopCharacteristics.getDataInputItemRef() != null + || multiInstanceLoopCharacteristics.getDataOutputItemRef() != null)) { + try { createDataInstancesForMultiInstance(activityDefinition, flowNodeInstance, expressionContext); - } else { + } catch (final SBonitaException e) { + throw new SActivityStateExecutionException( + "Failed to initialize multi instance variables of " + flowNodeInstance, + ScopedException.ITERATION, e); + } + } else { + try { createDataInstances(sDataDefinitions, flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE, expressionContext); + } catch (final SBonitaException e) { + throw new SActivityStateExecutionException("Failed to initialize variables of " + flowNodeInstance, + ScopedException.DATA, e); } - if (!sDataDefinitions.isEmpty() && log.isDebugEnabled()) { - final String message = "Initialized variables for flow node" - + LogMessageBuilder.buildFlowNodeContextMessage(flowNodeInstance); - log.debug(message); - } - return sDataDefinitions.size() > 0; - } catch (final SBonitaException e) { - throw new SActivityStateExecutionException(e); } + if (!sDataDefinitions.isEmpty() && log.isDebugEnabled()) { + final String message = "Initialized variables for flow node" + + LogMessageBuilder.buildFlowNodeContextMessage(flowNodeInstance); + log.debug(message); + } + return !sDataDefinitions.isEmpty(); + } protected void createDataInstancesForMultiInstance(final SActivityDefinition activityDefinition, diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java index f5d98eebcec..e0adaf1575c 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java @@ -45,6 +45,7 @@ import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; +import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.ConnectorService; @@ -245,7 +246,9 @@ public boolean registerConnectorsToExecute(final SProcessDefinition processDefin workService.registerWork(workFactory.createExecuteConnectorOfProcessDescriptor( processDefinitionId, sProcessInstance.getId(), sProcessInstance.getRootProcessInstanceId(), nextConnectorInstance.getId(), - sConnectorDefinition.getName(), activationEvent, + sConnectorDefinition.getConnectorId(), + sConnectorDefinition.getName(), + activationEvent, selectorForConnectorOnEnter)); return true; } @@ -404,30 +407,45 @@ protected boolean initialize(final long userId, final SProcessDefinition sProces final List connectors, final FlowNodeSelector selectorForConnectorOnEnter, final Map processInputs) throws SBonitaException { - SExpressionContext expressionContext = createExpressionsContextForProcessInstance(sProcessDefinition, sProcessInstance); - operations = operations != null ? new ArrayList<>(operations) : Collections.emptyList(); - storeProcessInstantiationInputs(sProcessInstance.getId(), processInputs); + operations = operations != null ? new ArrayList<>(operations) : Collections.emptyList(); + try { + storeProcessInstantiationInputs(sProcessInstance.getId(), processInputs); - // Create SDataInstances - bpmInstancesCreator.createDataInstances(sProcessInstance, processContainer, sProcessDefinition, - expressionContext, operations, context, - expressionContextToEvaluateOperations); + // Create SDataInstances + bpmInstancesCreator.createDataInstances(sProcessInstance, processContainer, sProcessDefinition, + expressionContext, operations, context, + expressionContextToEvaluateOperations); - initializeBusinessData(processContainer, sProcessInstance, expressionContext); - initializeStringIndexes(sProcessInstance, sProcessDefinition, processContainer); + initializeBusinessData(processContainer, sProcessInstance, expressionContext); + initializeStringIndexes(sProcessInstance, sProcessDefinition, processContainer); - createDocuments(sProcessDefinition, processContainer, sProcessInstance, userId, expressionContext, context); - createDocumentLists(processContainer, sProcessInstance, userId, expressionContext, context); + createDocuments(sProcessDefinition, processContainer, sProcessInstance, userId, expressionContext, context); + createDocumentLists(processContainer, sProcessInstance, userId, expressionContext, context); + } catch (SBonitaException e) { + e.setScope(ScopedException.DATA); + throw e; + } if (connectors != null) { //these are set only when start process through the command ExecuteActionsAndStartInstanceExt - executeConnectors(sProcessDefinition, sProcessInstance, connectors); + try { + executeConnectors(sProcessDefinition, sProcessInstance, connectors); + } catch (SBonitaException e) { + e.setScope(ScopedException.CONNECTOR); + throw e; + } } // operations given to the startProcess method of the API or by command, not operations of the process definition - executeOperations(operations, context, expressionContext, expressionContextToEvaluateOperations, - sProcessInstance); + try { + executeOperations(operations, context, expressionContext, expressionContextToEvaluateOperations, + sProcessInstance); + } catch (SBonitaException e) { + // Data mapping of call activity + e.setScope(ScopedException.DATA); + throw e; + } // Create connectors bpmInstancesCreator.createConnectorInstances(sProcessInstance, processContainer.getConnectors(), diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/StateBehaviors.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/StateBehaviors.java index 7a34a4e9add..d732d858a55 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/StateBehaviors.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/StateBehaviors.java @@ -31,6 +31,7 @@ import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceModificationException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException; @@ -210,17 +211,19 @@ public void mapDataOutputOfMultiInstance(final SProcessDefinition processDefinit if (businessData == null) { mapDataOutputOfMultiInstance(flowNodeInstance, miLoop); } else { - MapMultiInstanceBusinessDataOutput(flowNodeInstance, miLoop); + mapMultiInstanceBusinessDataOutput(flowNodeInstance, miLoop); } } } catch (final SBonitaException sbe) { - throw new SActivityStateExecutionException(sbe); + throw new SActivityStateExecutionException( + "Error while mapping multi instance output of " + flowNodeInstance, + ScopedException.ITERATION, sbe); } } } } - private void MapMultiInstanceBusinessDataOutput(final SFlowNodeInstance flowNodeInstance, + private void mapMultiInstanceBusinessDataOutput(final SFlowNodeInstance flowNodeInstance, final SMultiInstanceLoopCharacteristics miLoop) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException, SRefBusinessDataInstanceModificationException { @@ -270,25 +273,34 @@ public void mapActors(final SFlowNodeInstance flowNodeInstance, throws SActivityStateExecutionException { if (SFlowNodeType.USER_TASK.equals(flowNodeInstance.getType()) || SFlowNodeType.MANUAL_TASK.equals(flowNodeInstance.getType())) { - try { - final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) processContainer - .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); - if (humanTaskDefinition != null) { - final String actorName = humanTaskDefinition.getActorName(); - final long processDefinitionId = flowNodeInstance.getLogicalGroup(0); - final SUserFilterDefinition sUserFilterDefinition = humanTaskDefinition.getSUserFilterDefinition(); - if (sUserFilterDefinition != null) { + final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) processContainer + .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); + if (humanTaskDefinition != null) { + final String actorName = humanTaskDefinition.getActorName(); + final long processDefinitionId = flowNodeInstance.getLogicalGroup(0); + final SUserFilterDefinition sUserFilterDefinition = humanTaskDefinition.getSUserFilterDefinition(); + if (sUserFilterDefinition != null) { + try { mapUsingUserFilters(flowNodeInstance, humanTaskDefinition, actorName, processDefinitionId, sUserFilterDefinition); - } else { + } catch (SActivityStateExecutionException e) { + throw e; + } catch (SBonitaException e) { + throw new SActivityStateExecutionException( + "Error while mapping actor '" + actorName + "' with filter " + + sUserFilterDefinition.getUserFilterId() + " for " + flowNodeInstance, + ScopedException.ACTOR_MAPPING, e); + } + } else { + try { mapUsingActors(flowNodeInstance, actorName, processDefinitionId); + } catch (SBonitaException e) { + throw new SActivityStateExecutionException("Error while mapping actors for " + flowNodeInstance, + ScopedException.ACTOR_MAPPING, e); } } - } catch (final SActivityStateExecutionException e) { - throw e; - } catch (final Exception e) { - throw new SActivityStateExecutionException(e); } + } } @@ -319,8 +331,9 @@ void mapUsingUserFilters(final SFlowNodeInstance flowNodeInstance, final SHumanT final List userIds = result.getResult(); if (userIds == null || userIds.isEmpty() || userIds.contains(0L) || userIds.contains(-1L)) { throw new SActivityStateExecutionException( - "no user id returned by the user filter " + sUserFilterDefinition + " on activity " - + humanTaskDefinition.getName()); + "No user id returned by the user filter " + sUserFilterDefinition + " on activity " + + humanTaskDefinition.getName(), + ScopedException.ACTOR_MAPPING); } for (final Long userId : new TreeSet<>(userIds)) { final SPendingActivityMapping mapping = SPendingActivityMapping.builder() @@ -347,7 +360,8 @@ public void registerWaitingEvent(final SProcessDefinition processDefinition, eventsHandler.handleCatchEvent(processDefinition, intermediateCatchEventDefinition, intermediateCatchEventInstance); } catch (final SBonitaException e) { - throw new SActivityStateExecutionException("unable to handle catch event " + flowNodeInstance, e); + throw new SActivityStateExecutionException("unable to handle catch event " + flowNodeInstance, + ScopedException.EVENT, e); } } else if (flowNodeInstance instanceof SReceiveTaskInstance receiveTaskInstance) { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); @@ -357,7 +371,8 @@ public void registerWaitingEvent(final SProcessDefinition processDefinition, try { eventsHandler.handleCatchMessage(processDefinition, receiveTaskIDefinition, receiveTaskInstance); } catch (final SBonitaException e) { - throw new SActivityStateExecutionException("unable to handle catch event " + flowNodeInstance, e); + throw new SActivityStateExecutionException("unable to handle catch event " + flowNodeInstance, + ScopedException.EVENT, e); } } } @@ -377,7 +392,9 @@ public void handleBoundaryEvent(final SProcessDefinition processDefinition, .getBoundaryEventDefinition(boundaryInstance.getName()); eventsHandler.handleCatchEvent(processDefinition, boundaryEventDefinition, boundaryInstance); } catch (final SBonitaException e) { - throw new SActivityStateExecutionException("unable to handle catch event " + boundaryInstance, e); + throw new SActivityStateExecutionException("Unable to handle catch event " + boundaryInstance, + ScopedException.EVENT, + e); } } @@ -391,35 +408,37 @@ public void createData(final SProcessDefinition processDefinition, final SFlowNo public void updateDisplayNameAndDescription(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { - try { - final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); - final SFlowNodeDefinition flowNode = processContainer - .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); - if (flowNode != null) { - final SExpression displayNameExpression = flowNode.getDisplayName(); - final SExpression displayDescriptionExpression = flowNode.getDisplayDescription(); - final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(), - DataInstanceContainer.ACTIVITY_INSTANCE.name(), - processDefinition.getId()); - final String displayName; + final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); + final SFlowNodeDefinition flowNode = processContainer + .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); + if (flowNode != null) { + final SExpression displayNameExpression = flowNode.getDisplayName(); + final SExpression displayDescriptionExpression = flowNode.getDisplayDescription(); + final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(), + DataInstanceContainer.ACTIVITY_INSTANCE.name(), + processDefinition.getId()); + try { + String displayName = flowNode.getName(); if (displayNameExpression != null) { displayName = (String) expressionResolverService.evaluate(displayNameExpression, sExpressionContext); - } else { - displayName = flowNode.getName(); } - final String displayDescription; + activityInstanceService.updateDisplayName(flowNodeInstance, displayName); + } catch (SBonitaException e) { + throw new SActivityStateExecutionException("Error while updating display name", + ScopedException.GENERAL_INFORMATION, e); + } + try { + String displayDescription = flowNode.getDescription(); if (displayDescriptionExpression != null) { displayDescription = (String) expressionResolverService.evaluate(displayDescriptionExpression, sExpressionContext); - } else { - displayDescription = flowNode.getDescription(); } - activityInstanceService.updateDisplayName(flowNodeInstance, displayName); activityInstanceService.updateDisplayDescription(flowNodeInstance, displayDescription); + } catch (SBonitaException e) { + throw new SActivityStateExecutionException("Error while updating display description", + ScopedException.GENERAL_INFORMATION, e); } - } catch (final SBonitaException e) { - throw new SActivityStateExecutionException("error while updating display name and description", e); } } @@ -448,23 +467,24 @@ public void updateExpectedDuration(final SProcessDefinition processDefinition, activityInstanceService.setExpectedEndDate(flowNodeInstance, System.currentTimeMillis() + duration); } } catch (final SBonitaException e) { - throw new SActivityStateExecutionException("error while updating expected end date", e); + throw new SActivityStateExecutionException("Error while updating expected end date", + ScopedException.GENERAL_INFORMATION, e); } } public void updateDisplayDescriptionAfterCompletion(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { - try { - final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); - final SFlowNodeDefinition flowNode = processContainer - .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); - if (flowNode != null) { - final SExpression displayDescriptionAfterCompletionExpression = flowNode - .getDisplayDescriptionAfterCompletion(); - final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(), - DataInstanceContainer.ACTIVITY_INSTANCE.name(), - processDefinition.getId()); + final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); + final SFlowNodeDefinition flowNode = processContainer + .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); + if (flowNode != null) { + final SExpression displayDescriptionAfterCompletionExpression = flowNode + .getDisplayDescriptionAfterCompletion(); + final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(), + DataInstanceContainer.ACTIVITY_INSTANCE.name(), + processDefinition.getId()); + try { final String displayDescriptionAfterCompletion; if (displayDescriptionAfterCompletionExpression != null) { displayDescriptionAfterCompletion = (String) expressionResolverService.evaluate( @@ -473,9 +493,10 @@ public void updateDisplayDescriptionAfterCompletion(final SProcessDefinition pro activityInstanceService.updateDisplayDescription(flowNodeInstance, displayDescriptionAfterCompletion); } + } catch (final SBonitaException e) { + throw new SActivityStateExecutionException("Error while updating display description", + ScopedException.GENERAL_INFORMATION, e); } - } catch (final SBonitaException e) { - throw new SActivityStateExecutionException("error while updating display name and description", e); } } @@ -493,7 +514,8 @@ public void executeOperations(final SProcessDefinition processDefinition, final operationService.execute(sOperations, sExpressionContext); } } catch (final SOperationExecutionException e) { - throw new SActivityStateExecutionException(e); + throw new SActivityStateExecutionException("Error while executing operations", + ScopedException.OPERATION, e); } } @@ -506,7 +528,8 @@ public void handleThrowEvent(final SProcessDefinition processDefinition, final S try { eventsHandler.handleThrowEvent(processDefinition, eventDefinition, throwEventInstance); } catch (final SBonitaException e) { - throw new SActivityStateExecutionException("unable to handle throw event " + flowNodeInstance, e); + throw new SActivityStateExecutionException("Unable to handle throw event " + flowNodeInstance, + ScopedException.EVENT, e); } } else if (SFlowNodeType.SEND_TASK.equals(flowNodeInstance.getType())) { final SSendTaskInstance sendTaskInstance = (SSendTaskInstance) flowNodeInstance; @@ -516,7 +539,8 @@ public void handleThrowEvent(final SProcessDefinition processDefinition, final S try { eventsHandler.handleThrowMessage(processDefinition, sendTaskDefinition, sendTaskInstance); } catch (final SBonitaException e) { - throw new SActivityStateExecutionException("unable to handle throw message " + flowNodeInstance, e); + throw new SActivityStateExecutionException("Unable to handle throw message " + flowNodeInstance, + ScopedException.EVENT, e); } } } @@ -555,12 +579,17 @@ public void executeConnectorInWork(final Long processDefinitionId, final long pr connectorInstanceService.setState(connector, ConnectorState.EXECUTING.name()); workService.registerWork(workFactory.createExecuteConnectorOfActivityDescriptor(processDefinitionId, processInstanceId, flowNodeDefinitionId, - flowNodeInstanceId, connectorInstanceId, connectorDefinitionName)); + flowNodeInstanceId, connectorInstanceId, + sConnectorDefinition.getConnectorId(), + connectorDefinitionName, + sConnectorDefinition.getActivationEvent().name())); } catch (final SConnectorInstanceModificationException e) { - throw new SActivityStateExecutionException("Unable to set ConnectorState to EXECUTING", e); + throw new SActivityStateExecutionException("Unable to set ConnectorState to EXECUTING", + ScopedException.CONNECTOR, e); } catch (final SWorkRegisterException e) { throw new SActivityStateExecutionException( "Unable to register the work that execute the connector " + connector + " on " + flowNodeInstanceId, + ScopedException.CONNECTOR, e); } } @@ -598,7 +627,8 @@ private void createAttachedBoundaryEvents(final SProcessDefinition processDefini } } catch (final SBonitaException e) { throw new SActivityStateExecutionException( - "Unable to create boundary events attached to activity " + activityInstance.getName(), e); + "Unable to create boundary events attached to activity " + activityInstance.getName(), + ScopedException.EVENT, e); } } @@ -663,7 +693,8 @@ public void interruptAttachedBoundaryEvent(final SProcessDefinition processDefin } } catch (final SBonitaException e) { throw new SActivityStateExecutionException( - "Unable to cancel boundary events attached to activity " + activityInstance.getName(), e); + "Unable to cancel boundary events attached to activity " + activityInstance.getName(), + ScopedException.EVENT, e); } } @@ -676,7 +707,9 @@ public void addAssignmentSystemCommentIfTaskWasAutoAssign(final SFlowNodeInstanc try { addAssignmentSystemComment(flowNodeInstance, userId); } catch (final SBonitaException e) { - throw new SActivityStateExecutionException("error while updating display name and description", e); + throw new SActivityStateExecutionException( + "Error while adding a comment on task " + flowNodeInstance, + ScopedException.GENERAL_INFORMATION, e); } } } @@ -734,7 +767,8 @@ public int getNumberOfInstancesToCreateFromInputRef(final SProcessDefinition pro return refBusinessDataService.getNumberOfDataOfMultiRefBusinessData(businessData.getName(), flowNodeInstance.getParentProcessInstanceId()); } catch (final SBonitaReadException sbre) { - throw new SActivityStateExecutionException(sbre); + throw new SActivityStateExecutionException("Error while counting number of multi instances to create", + ScopedException.ITERATION, sbre); } } @@ -754,7 +788,8 @@ private int getNumberOfInstanceToCreateFromSimpleData(final SProcessDefinition p throw new SActivityStateExecutionException( "The multi instance on activity " + flowNodeInstance.getName() + " of process " + processDefinition.getName() + " " + processDefinition.getVersion() - + " have a loop data input which is not a java.util.List"); + + " have a loop data input which is not a java.util.List", + ScopedException.ITERATION); } return numberOfInstanceMax; } @@ -801,7 +836,7 @@ public void updateOutputData(final SProcessDefinition processDefinition, final S throw new SActivityStateExecutionException("The multi instance on activity " + flowNodeInstance.getName() + " of process " + processDefinition.getName() + " " + processDefinition.getVersion() - + " have a loop data output which is not a java.util.List"); + + " have a loop data output which is not a java.util.List", ScopedException.ITERATION); } } } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/TransitionEvaluator.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/TransitionEvaluator.java index dc24310a266..b06b94a4a44 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/TransitionEvaluator.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/TransitionEvaluator.java @@ -60,6 +60,7 @@ protected List evaluateOutgoingTransitions(FlowNodeTransi final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), sDefinition.getId()); + sExpressionContext.setProcessDefinition(sDefinition); if (SFlowNodeType.GATEWAY.equals(flowNodeInstance.getType())) { return evaluateOutgoingTransitionsForGateways(transitions, sDefinition, flowNodeInstance, sExpressionContext); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/MessageEventHandlerStrategy.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/MessageEventHandlerStrategy.java index 30eb0de4cf0..813ae4493f8 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/MessageEventHandlerStrategy.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/MessageEventHandlerStrategy.java @@ -136,7 +136,6 @@ public void handleCatchEvent(final SProcessDefinition processDefinition, final S fillCorrelation(builder, messageTrigger.getCorrelations(), expressionContext); getEventInstanceService().createWaitingEvent(builder.done()); messagesHandlingService.triggerMatchingOfMessages(); - } public void handleCatchEvent(final SProcessDefinition processDefinition, @@ -158,9 +157,14 @@ public void handleCatchEvent(final SProcessDefinition processDefinition, getParentContainerType(receiveTaskInstance).name(), processDefinition.getId()); - fillCorrelation(builder, messageTrigger.getCorrelations(), expressionContext); - getEventInstanceService().createWaitingEvent(builder.done()); - messagesHandlingService.triggerMatchingOfMessages(); + try { + fillCorrelation(builder, messageTrigger.getCorrelations(), expressionContext); + getEventInstanceService().createWaitingEvent(builder.done()); + messagesHandlingService.triggerMatchingOfMessages(); + } catch (SBonitaException e) { + e.setMessageInstanceNameOnContext(messageTrigger.getMessageName()); + throw e; + } } @Override @@ -171,8 +175,14 @@ public void handleThrowEvent(final SProcessDefinition processDefinition, final S final SExpressionContext expressionContext = new SExpressionContext(eventInstance.getParentContainerId(), getParentContainerType(eventInstance).name(), processDefinitionId); - - handleThrowMessage(sEventTriggerDefinition, eventInstance.getName(), processDefinitionId, expressionContext); + try { + handleThrowMessage(sEventTriggerDefinition, eventInstance.getName(), processDefinitionId, + expressionContext); + } catch (SBonitaException e) { + e.setMessageInstanceNameOnContext( + ((SThrowMessageEventTriggerDefinition) sEventTriggerDefinition).getMessageName()); + throw e; + } } public void handleThrowEvent(final SProcessDefinition processDefinition, final SSendTaskInstance sendTaskInstance, @@ -181,7 +191,13 @@ public void handleThrowEvent(final SProcessDefinition processDefinition, final S final SExpressionContext expressionContext = new SExpressionContext(sendTaskInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId()); - handleThrowMessage(messageTrigger, sendTaskInstance.getName(), processDefinition.getId(), expressionContext); + try { + handleThrowMessage(messageTrigger, sendTaskInstance.getName(), processDefinition.getId(), + expressionContext); + } catch (SBonitaException e) { + e.setMessageInstanceNameOnContext(messageTrigger.getMessageName()); + throw e; + } } private void handleThrowMessage(final SEventTriggerDefinition sEventTriggerDefinition, diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletingCallActivityState.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletingCallActivityState.java index cc4c02886ca..34b2c4dc941 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletingCallActivityState.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletingCallActivityState.java @@ -14,6 +14,7 @@ package org.bonitasoft.engine.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition; @@ -71,7 +72,8 @@ private void executeDataOutputOperations(final SProcessDefinition processDefinit // archive child process instance bpmArchiverService.archiveAndDeleteProcessInstance(childProcInst); } catch (final SBonitaException e) { - throw new SActivityStateExecutionException(e); + throw new SActivityStateExecutionException("Unable to map call activity output data", + ScopedException.OPERATION, e); } } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingLoopActivityState.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingLoopActivityState.java index d0da8fe109e..f47778a8fea 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingLoopActivityState.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingLoopActivityState.java @@ -20,6 +20,7 @@ import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; @@ -136,7 +137,7 @@ public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDe } return !loop; } catch (final SBonitaException e) { - throw new SActivityStateExecutionException(e); + throw new SActivityStateExecutionException("Unable to handle loop activity", ScopedException.ITERATION, e); } } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingActivityWithBoundaryEventsState.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingActivityWithBoundaryEventsState.java index 445650c6ecd..bc3f24f2941 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingActivityWithBoundaryEventsState.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingActivityWithBoundaryEventsState.java @@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; @@ -162,7 +163,7 @@ private void handleCallActivity(SProcessDefinition processDefinition, SFlowNodeI final SCallActivityDefinition callActivity = (SCallActivityDefinition) processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); if (callActivity == null) { - throw new SActivityStateExecutionException("unable to find call activity definition with name " + throw new SActivityStateExecutionException("Unable to find call activity definition with name " + flowNodeInstance.getName() + " in process definition " + processDefinition.getId()); } @@ -217,7 +218,7 @@ public void afterCompletion(final int txState) { }); } } catch (final SBonitaException e) { - throw new SActivityStateExecutionException(e); + throw new SActivityStateExecutionException("Unable to handle call activity", ScopedException.DATA, e); } } } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingMultiInstanceActivityState.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingMultiInstanceActivityState.java index 5a6bd31f077..7cc329d220c 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingMultiInstanceActivityState.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingMultiInstanceActivityState.java @@ -14,6 +14,7 @@ package org.bonitasoft.engine.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; @@ -77,7 +78,8 @@ public StateCode execute(final SProcessDefinition processDefinition, final SFlow throw new SActivityStateExecutionException( "The multi instance on activity " + flowNodeInstance.getName() + " of process " + processDefinition.getName() + " " + processDefinition.getVersion() - + " did not have loop cardinality nor loop data input ref set"); + + " did not have loop cardinality nor loop data input ref set", + ScopedException.ITERATION); } stateBehaviors.updateOutputData(processDefinition, multiInstanceActivityInstance, miLoop, numberOfInstanceMax); @@ -90,7 +92,8 @@ public StateCode execute(final SProcessDefinition processDefinition, final SFlow } catch (final SActivityStateExecutionException e) { throw e; } catch (final SBonitaException e) { - throw new SActivityStateExecutionException(e); + throw new SActivityStateExecutionException("Failed to execute multi instance activity " + flowNodeInstance, + ScopedException.ITERATION, e); } return StateCode.DONE; } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/STransitionConditionEvaluationException.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/STransitionConditionEvaluationException.java new file mode 100644 index 00000000000..650fca57ff3 --- /dev/null +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/STransitionConditionEvaluationException.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.execution.transition; + +import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.SExceptionContext; +import org.bonitasoft.engine.commons.exceptions.ScopedException; + +public class STransitionConditionEvaluationException extends SBonitaException { + + public STransitionConditionEvaluationException(String message, String transitionName, String targetFlowNode, + Throwable cause) { + super(message, ScopedException.OUTGOING_TRANSITION, cause); + if (transitionName != null) { + getContext().put(SExceptionContext.TRANSITION_NAME, transitionName); + } + if (targetFlowNode != null) { + getContext().put(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME, targetFlowNode); + } + } + +} diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluator.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluator.java index 19a997b6725..281a320ea4e 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluator.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluator.java @@ -13,6 +13,8 @@ **/ package org.bonitasoft.engine.execution.transition; +import java.util.Objects; + import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; @@ -38,11 +40,41 @@ public Boolean evaluateCondition(final STransitionDefinition sTransitionDefiniti return null; } if (!Boolean.class.getName().equals(condition.getReturnType())) { - throw new SExpressionEvaluationException( - "Condition expression must return a boolean, on transition: " + sTransitionDefinition.getName(), - condition.getName()); + throw new STransitionConditionEvaluationException( + "Condition expression must return a boolean", + getTransitionName(sTransitionDefinition), + getTargetFlowNode(sTransitionDefinition, contextDependency), + new SExpressionEvaluationException("Invalid expression return type", condition.getName())); + } + try { + return (Boolean) resolverService.evaluate(condition, contextDependency); + } catch (SBonitaException e) { + throw new STransitionConditionEvaluationException( + "Unable to evaluate transition condition", + getTransitionName(sTransitionDefinition), + getTargetFlowNode(sTransitionDefinition, contextDependency), e); + } + } + + private static String getTransitionName(STransitionDefinition transition) { + if (Objects.equals(transition.getName(), transition.getSource() + "_->_" + transition.getTarget())) { + return null; + } + return transition.getName(); + } + + private static String getTargetFlowNode(STransitionDefinition sTransitionDefinition, + SExpressionContext contextDependency) { + if (contextDependency.getProcessDefinition() != null + && contextDependency.getProcessDefinition().getProcessContainer() != null) { + var targetFlowNode = contextDependency.getProcessDefinition().getProcessContainer() + .getFlowNode(sTransitionDefinition.getTarget()); + if (targetFlowNode == null) { + return null; + } + return targetFlowNode.getType().name().toLowerCase() + "::" + targetFlowNode.getName(); } - return (Boolean) resolverService.evaluate(condition, contextDependency); + return null; } } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/BPMWorkFactory.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/BPMWorkFactory.java index 24d9d3aae9b..1a85733c1a0 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/BPMWorkFactory.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/BPMWorkFactory.java @@ -60,6 +60,7 @@ public class BPMWorkFactory implements WorkFactory { private static final String FLOW_NODE_INSTANCE_ID = "flowNodeInstanceId"; private static final String CONNECTOR_INSTANCE_ID = "connectorInstanceId"; private static final String CONNECTOR_DEFINITION_NAME = "connectorDefinitionName"; + private static final String CONNECTOR_DEFINITION_ID = "connectorDefinitionId"; private static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; public static final String STATE_ID = "stateId"; private static final String STATE_EXECUTING = "stateExecuting"; @@ -86,26 +87,33 @@ private BonitaWork createExecuteConnectorOfActivity(WorkDescriptor workDescripto final long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID); final long flowNodeInstanceId = workDescriptor.getLong(FLOW_NODE_INSTANCE_ID); final long connectorInstanceId = workDescriptor.getLong(CONNECTOR_INSTANCE_ID); + final String connectorDefinitionId = workDescriptor.getString(CONNECTOR_DEFINITION_ID); final String connectorDefinitionName = workDescriptor.getString(CONNECTOR_DEFINITION_NAME); + final ConnectorEvent activationEvent = ConnectorEvent.valueOf(workDescriptor.getString(ACTIVATION_EVENT)); BonitaWork wrappedWork = new ExecuteConnectorOfActivity(processDefinitionId, processInstanceId, workDescriptor.getLong(FLOW_NODE_DEFINITION_ID), flowNodeInstanceId, connectorInstanceId, connectorDefinitionName); - wrappedWork = new ConnectorDefinitionAndInstanceContextWork(wrappedWork, connectorDefinitionName, - connectorInstanceId); + wrappedWork = new ConnectorDefinitionAndInstanceContextWork(wrappedWork, connectorDefinitionId, + connectorDefinitionName, + connectorInstanceId, activationEvent); wrappedWork = withFlowNodeContext(processDefinitionId, processInstanceId, flowNodeInstanceId, wrappedWork); return withSession(wrappedWork); } public WorkDescriptor createExecuteConnectorOfActivityDescriptor(final long processDefinitionId, final long processInstanceId, final long flowNodeDefinitionId, - final long flowNodeInstanceId, final long connectorInstanceId, final String connectorDefinitionName) { + final long flowNodeInstanceId, final long connectorInstanceId, + final String connectorDefinitionId, + final String connectorDefinitionName, final String activationEvent) { return WorkDescriptor.create(EXECUTE_ACTIVITY_CONNECTOR) .withParameter(PROCESS_DEFINITION_ID, processDefinitionId) .withParameter(PROCESS_INSTANCE_ID, processInstanceId) .withParameter(FLOW_NODE_DEFINITION_ID, flowNodeDefinitionId) .withParameter(FLOW_NODE_INSTANCE_ID, flowNodeInstanceId) .withParameter(CONNECTOR_INSTANCE_ID, connectorInstanceId) - .withParameter(CONNECTOR_DEFINITION_NAME, connectorDefinitionName); + .withParameter(CONNECTOR_DEFINITION_ID, connectorDefinitionId) + .withParameter(CONNECTOR_DEFINITION_NAME, connectorDefinitionName) + .withParameter(ACTIVATION_EVENT, activationEvent); } private BonitaWork createExecuteConnectorOfProcess(WorkDescriptor workDescriptor) { @@ -113,13 +121,15 @@ private BonitaWork createExecuteConnectorOfProcess(WorkDescriptor workDescriptor long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID); long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID); long connectorInstanceId = workDescriptor.getLong(CONNECTOR_INSTANCE_ID); + String connectorDefinitionId = workDescriptor.getString(CONNECTOR_DEFINITION_ID); String connectorDefinitionName = workDescriptor.getString(CONNECTOR_DEFINITION_NAME); ConnectorEvent activationEvent = (ConnectorEvent.valueOf(workDescriptor.getString(ACTIVATION_EVENT))); String flowNodeIds = workDescriptor.getString(FLOW_NODE_DEFINITIONS_FILTER); List flowNodeDefinitionsFilter = getListOfFlowNodeDefinitionsToStart(flowNodeIds); Long subProcessDefinitionId = workDescriptor.getLong(SUB_PROCESS_DEFINITION_ID); - BonitaWork wrappedWork = withConnectorContext(connectorInstanceId, connectorDefinitionName, activationEvent, + BonitaWork wrappedWork = withConnectorContext(connectorInstanceId, connectorDefinitionId, + connectorDefinitionName, activationEvent, withProcessContext(processDefinitionId, processInstanceId, rootProcessInstanceId, new ExecuteConnectorOfProcess(processDefinitionId, connectorInstanceId, connectorDefinitionName, @@ -144,13 +154,15 @@ private List getListOfFlowNodeDefinitionsToStart(String flowNodeIds) { public WorkDescriptor createExecuteConnectorOfProcessDescriptor(final long processDefinitionId, final long processInstanceId, final long rootProcessInstanceId, - final long connectorInstanceId, final String connectorDefinitionName, final ConnectorEvent activationEvent, + final long connectorInstanceId, final String connectorDefinitionId, final String connectorDefinitionName, + final ConnectorEvent activationEvent, final FlowNodeSelector flowNodeSelector) { return WorkDescriptor.create(EXECUTE_PROCESS_CONNECTOR) .withParameter(PROCESS_DEFINITION_ID, processDefinitionId) .withParameter(PROCESS_INSTANCE_ID, processInstanceId) .withParameter(ROOT_PROCESS_INSTANCE_ID, rootProcessInstanceId) .withParameter(CONNECTOR_INSTANCE_ID, connectorInstanceId) + .withParameter(CONNECTOR_DEFINITION_ID, connectorDefinitionId) .withParameter(CONNECTOR_DEFINITION_NAME, connectorDefinitionName) .withParameter(ACTIVATION_EVENT, activationEvent.name()) .withParameter(FLOW_NODE_DEFINITIONS_FILTER, @@ -164,10 +176,13 @@ private InSessionBonitaWork withSession(BonitaWork wrappedWork) { return new InSessionBonitaWork(wrappedWork); } - private BonitaWork withConnectorContext(long connectorInstanceId, String connectorDefinitionName, + private BonitaWork withConnectorContext(long connectorInstanceId, + String connectorDefinitionId, + String connectorDefinitionName, ConnectorEvent activationEvent, ProcessInstanceContextWork processInstanceContextWork) { - return new ConnectorDefinitionAndInstanceContextWork(processInstanceContextWork, connectorDefinitionName, + return new ConnectorDefinitionAndInstanceContextWork(processInstanceContextWork, connectorDefinitionId, + connectorDefinitionName, connectorInstanceId, activationEvent); } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfActivity.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfActivity.java index a8d53e88f95..0398b1c7be1 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfActivity.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfActivity.java @@ -17,6 +17,7 @@ import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.exception.SConnectorDefinitionNotFoundException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; @@ -30,6 +31,7 @@ import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; +import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; @@ -40,7 +42,6 @@ import org.bonitasoft.engine.execution.WaitingEventsInterrupter; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.service.ServiceAccessor; -import org.bonitasoft.engine.transaction.STransactionException; import org.bonitasoft.engine.work.WorkDescriptor; import org.bonitasoft.engine.work.WorkService; @@ -70,7 +71,7 @@ public class ExecuteConnectorOfActivity extends ExecuteConnectorWork { @Override protected void evaluateOutput(final Map context, final ConnectorResult result, final SConnectorDefinition sConnectorDefinition) - throws STransactionException, SBonitaException { + throws SBonitaException { evaluateOutput(context, result, sConnectorDefinition, flowNodeInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.name()); } @@ -87,15 +88,19 @@ protected void continueFlow(final Map context) throws SBonitaExc } @Override - protected void setContainerInFail(final Map context) throws SBonitaException { + protected void setContainerInFail(final Map context, Throwable t) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); WaitingEventsInterrupter waitingEventsInterrupter = new WaitingEventsInterrupter( serviceAccessor.getEventInstanceService(), serviceAccessor.getSchedulerService()); + var activityInstanceService = serviceAccessor.getActivityInstanceService(); FailedStateSetter failedStateSetter = new FailedStateSetter(waitingEventsInterrupter, - serviceAccessor.getActivityInstanceService(), + activityInstanceService, serviceAccessor.getFlowNodeStateManager()); failedStateSetter.setAsFailed(flowNodeInstanceId); + var failureService = serviceAccessor.getBpmFailureService(); + failureService.createFlowNodeFailure(activityInstanceService.getFlowNodeInstance(flowNodeInstanceId), + new BPMFailureService.Failure(ScopedException.CONNECTOR, t)); } @Override diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfProcess.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfProcess.java index 34307b650e7..715c0fdff38 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfProcess.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfProcess.java @@ -42,7 +42,11 @@ import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; -import org.bonitasoft.engine.execution.*; +import org.bonitasoft.engine.execution.Filter; +import org.bonitasoft.engine.execution.FlowNodeIdFilter; +import org.bonitasoft.engine.execution.FlowNodeSelector; +import org.bonitasoft.engine.execution.ProcessExecutor; +import org.bonitasoft.engine.execution.StartFlowNodeFilter; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ServiceAccessor; @@ -62,7 +66,7 @@ public class ExecuteConnectorOfProcess extends ExecuteConnectorWork { final Filter filterFlowNodeDefinitions; - private long subProcessDefinitionId; + private final long subProcessDefinitionId; ExecuteConnectorOfProcess(final long processDefinitionId, final long connectorInstanceId, final String connectorDefinitionName, @@ -114,7 +118,7 @@ protected void continueFlow(final Map context) throws SBonitaExc } @Override - protected void setContainerInFail(final Map context) throws SBonitaException { + protected void setContainerInFail(final Map context, Throwable t) throws SBonitaException { final ProcessInstanceService processInstanceService = getServiceAccessor(context).getProcessInstanceService(); final SProcessInstance intTxProcessInstance = processInstanceService.getProcessInstance(processInstanceId); processInstanceService.setState(intTxProcessInstance, ProcessInstanceState.ERROR); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWork.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWork.java index ddf8f307549..1773c1ca510 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWork.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWork.java @@ -60,14 +60,14 @@ public abstract class ExecuteConnectorWork extends TenantAwareBonitaWork { private final SExpressionContext inputParametersContext; private final long processInstanceId; - public ExecuteConnectorWork(final long processDefinitionId, final long connectorInstanceId, + protected ExecuteConnectorWork(final long processDefinitionId, final long connectorInstanceId, final String connectorDefinitionName, final SExpressionContext inputParametersContext, long processInstanceId) { this(processDefinitionId, connectorInstanceId, connectorDefinitionName, inputParametersContext, null, processInstanceId); } - public ExecuteConnectorWork(final long processDefinitionId, final long connectorInstanceId, + protected ExecuteConnectorWork(final long processDefinitionId, final long connectorInstanceId, final String connectorDefinitionName, final SExpressionContext inputParametersContext, final Map inputs, long processInstanceId) { super(); @@ -89,7 +89,7 @@ protected abstract SThrowEventInstance createThrowErrorEventInstance(Map context) throws SBonitaException; + protected abstract void setContainerInFail(Map context, Throwable t) throws SBonitaException; protected abstract void continueFlow(Map context) throws SBonitaException; @@ -105,7 +105,7 @@ protected ClassLoader getClassLoader(final Map context) throws S protected void setConnectorAndContainerToFailed(final Map context, final Throwable t) throws SBonitaException { setConnectorOnlyToFailed(context, t); - setContainerInFail(context); + setContainerInFail(context, t); } protected void setConnectorOnlyToFailed(final Map context, final Throwable t) @@ -155,7 +155,8 @@ public CompletableFuture work(final Map context) throws Ex .getConnectorImplementationDescriptor(); return connectorService.executeConnector(processDefinitionId, connectorInstance, connectorImplementationDescriptor, processClassloader, - callable.getInputParameters()).thenAccept(r -> { + callable.getInputParameters()) + .thenAccept(r -> { try { executeOutputOperationsAndContinue(context, serviceAccessor, userTransactionService, sConnectorDefinition, r); @@ -165,7 +166,6 @@ public CompletableFuture work(final Map context) throws Ex "Unable to evaluate output operations of connectors and continue", e)); } }); - } finally { if (timeTracker.isTrackable(TimeTrackerRecords.EXECUTE_CONNECTOR_WORK)) { final long endTime = System.currentTimeMillis(); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteFlowNodeWork.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteFlowNodeWork.java index 822d416662e..0dbb986e270 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteFlowNodeWork.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteFlowNodeWork.java @@ -16,8 +16,8 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; -import org.bonitasoft.engine.core.process.instance.api.BpmFailureService; -import org.bonitasoft.engine.core.process.instance.api.exceptions.FailureContext; +import org.bonitasoft.engine.commons.exceptions.ScopedException; +import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; 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.model.SFlowNodeInstance; @@ -105,14 +105,14 @@ public void handleFailure(final Throwable e, final Map context) userTransactionService.executeInTransaction(new SetInFailCallable(failedStateSetter, flowNodeInstance, serviceAccessor.getBpmFailureService(), - failureContext(e))); + createFailure(e))); } - private FailureContext failureContext(Throwable e) { - if (e instanceof FailureContext failureContext) { - return failureContext; + private BPMFailureService.Failure createFailure(Throwable e) { + if (e instanceof ScopedException scopedException) { + return new BPMFailureService.Failure(scopedException.getScope(), e); } - return () -> new BpmFailureService.Failure(FailureContext.UNKNOWN_SCOPE, FailureContext.EMPTY_CONTEXT, e); + return new BPMFailureService.Failure(ScopedException.UNKNOWN_SCOPE, e); } @Override diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/NotifyChildFinishedWork.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/NotifyChildFinishedWork.java index ef896b58eb6..f5d0ae48aaf 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/NotifyChildFinishedWork.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/NotifyChildFinishedWork.java @@ -19,8 +19,8 @@ import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.commons.exceptions.SBonitaException; -import org.bonitasoft.engine.core.process.instance.api.BpmFailureService; -import org.bonitasoft.engine.core.process.instance.api.exceptions.FailureContext; +import org.bonitasoft.engine.commons.exceptions.ScopedException; +import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; 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.model.SFlowNodeInstance; @@ -124,14 +124,14 @@ public void handleFailure(final Throwable e, final Map context) serviceAccessor.getFlowNodeStateManager()); SFlowNodeInstance flowNodeInstance = (SFlowNodeInstance) context.get("flowNodeInstance"); userTransactionService.executeInTransaction(new SetInFailCallable(failedStateSetter, flowNodeInstance, - serviceAccessor.getBpmFailureService(), failureContext(e))); + serviceAccessor.getBpmFailureService(), createFailure(e))); } - private FailureContext failureContext(Throwable e) { - if (e instanceof FailureContext failureContext) { - return failureContext; + private BPMFailureService.Failure createFailure(Throwable e) { + if (e instanceof ScopedException scopedException) { + return new BPMFailureService.Failure(scopedException.getScope(), e); } - return () -> new BpmFailureService.Failure(FailureContext.UNKNOWN_SCOPE, FailureContext.EMPTY_CONTEXT, e); + return new BPMFailureService.Failure(ScopedException.UNKNOWN_SCOPE, e); } @Override diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/SetInFailCallable.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/SetInFailCallable.java index ed3417f4a4a..50d28c264ab 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/SetInFailCallable.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/SetInFailCallable.java @@ -15,8 +15,7 @@ import java.util.concurrent.Callable; -import org.bonitasoft.engine.core.process.instance.api.BpmFailureService; -import org.bonitasoft.engine.core.process.instance.api.exceptions.FailureContext; +import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; /** @@ -26,21 +25,21 @@ public class SetInFailCallable implements Callable { private final FailedStateSetter failedStateSetter; private final SFlowNodeInstance flowNodeInstance; - private final BpmFailureService bpmFailureService; - private final FailureContext failureContext; + private final BPMFailureService bpmFailureService; + private final BPMFailureService.Failure failure; SetInFailCallable(FailedStateSetter failedStateSetter, final SFlowNodeInstance flowNodeInstance, - BpmFailureService bpmFailureService, FailureContext failureContext) { + BPMFailureService bpmFailureService, BPMFailureService.Failure failure) { this.failedStateSetter = failedStateSetter; this.flowNodeInstance = flowNodeInstance; this.bpmFailureService = bpmFailureService; - this.failureContext = failureContext; + this.failure = failure; } @Override public Void call() throws Exception { failedStateSetter.setAsFailed(flowNodeInstance.getId()); - bpmFailureService.createFlowNodeFailure(flowNodeInstance, failureContext.createFailure()); + bpmFailureService.createFlowNodeFailure(flowNodeInstance, failure); return null; } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWork.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWork.java index a8264fad6aa..c7a91781238 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWork.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWork.java @@ -16,7 +16,7 @@ import java.util.Map; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; -import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.work.BonitaWork; /** @@ -28,7 +28,9 @@ public class ConnectorDefinitionAndInstanceContextWork extends TxInHandleFailure private static final long serialVersionUID = 6958842321501639910L; - private final String connectorDefinitionName; + private final String connectorDefinitionId; + + private final String connectorName; private final ConnectorEvent activationEvent; @@ -37,37 +39,31 @@ public class ConnectorDefinitionAndInstanceContextWork extends TxInHandleFailure /** * @param wrappedWork * The work to wrap - * @param connectorDefinitionName - * The name of the connector definition - * @param connectorInstanceId - * The identifier of the connector instance - */ - public ConnectorDefinitionAndInstanceContextWork(final BonitaWork wrappedWork, final String connectorDefinitionName, - final long connectorInstanceId) { - this(wrappedWork, connectorDefinitionName, connectorInstanceId, null); - } - - /** - * @param wrappedWork - * The work to wrap - * @param connectorDefinitionName + * @param connectorDefinitionId * The name of the connector definition + * @param connectorName + * The name of the connector * @param connectorInstanceId * The identifier of the connector instance * @param activationEvent * The event to activate the connector */ - public ConnectorDefinitionAndInstanceContextWork(final BonitaWork wrappedWork, final String connectorDefinitionName, + public ConnectorDefinitionAndInstanceContextWork(final BonitaWork wrappedWork, + final String connectorDefinitionId, + final String connectorName, long connectorInstanceId, final ConnectorEvent activationEvent) { super(wrappedWork); - this.connectorDefinitionName = connectorDefinitionName; + this.connectorDefinitionId = connectorDefinitionId; + this.connectorName = connectorName; this.activationEvent = activationEvent; this.connectorInstanceId = connectorInstanceId; } @Override - protected void setExceptionContext(final SBonitaException e, final Map context) { - e.setConnectorImplementationClassNameOnContext(connectorDefinitionName); + protected void setExceptionContext(final ExceptionContext e, final Map context) { + e.setConnectorImplementationClassNameOnContext(connectorName); + e.setConnectorNameOnContext(connectorName); + e.setConnectorDefinitionIdOnContext(connectorDefinitionId); e.setConnectorInstanceIdOnContext(connectorInstanceId); if (activationEvent != null) { e.setConnectorActivationEventOnContext(activationEvent.name()); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/FlowNodeDefinitionAndInstanceContextWork.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/FlowNodeDefinitionAndInstanceContextWork.java index 4bc2668c1e6..3cf4e3f3195 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/FlowNodeDefinitionAndInstanceContextWork.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/FlowNodeDefinitionAndInstanceContextWork.java @@ -15,6 +15,7 @@ import java.util.Map; +import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; @@ -46,7 +47,7 @@ public FlowNodeDefinitionAndInstanceContextWork(final WrappingBonitaWork wrapped } @Override - protected void setExceptionContext(final SBonitaException sBonitaException, final Map context) + protected void setExceptionContext(final ExceptionContext sBonitaException, final Map context) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/MessageInstanceContextWork.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/MessageInstanceContextWork.java index a5e7e8c51f9..36e5485615c 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/MessageInstanceContextWork.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/MessageInstanceContextWork.java @@ -15,7 +15,7 @@ import java.util.Map; -import org.bonitasoft.engine.commons.exceptions.SBonitaException; +import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.work.BonitaWork; /** @@ -39,7 +39,7 @@ public MessageInstanceContextWork(BonitaWork work, String messageName, String ta } @Override - protected void setExceptionContext(final SBonitaException sBonitaException, final Map context) { + protected void setExceptionContext(final ExceptionContext sBonitaException, final Map context) { sBonitaException.setMessageInstanceNameOnContext(messageName); sBonitaException.setMessageInstanceTargetProcessOnContext(targetProcess); sBonitaException.setMessageInstanceTargetFlowNodeOnContext(targetFlowNode); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessDefinitionContextWork.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessDefinitionContextWork.java index e06d4eaf2d8..8b378070974 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessDefinitionContextWork.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessDefinitionContextWork.java @@ -15,6 +15,7 @@ import java.util.Map; +import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; @@ -44,7 +45,7 @@ public ProcessDefinitionContextWork(final BonitaWork wrappedWork, final long pro } @Override - protected void setExceptionContext(final SBonitaException sBonitaException, final Map context) + protected void setExceptionContext(final ExceptionContext sBonitaException, final Map context) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessInstanceContextWork.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessInstanceContextWork.java index bc96544b99f..a5684165a21 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessInstanceContextWork.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessInstanceContextWork.java @@ -15,6 +15,7 @@ import java.util.Map; +import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; @@ -70,7 +71,7 @@ protected void setRootProcessInstanceId(final long id) { } @Override - protected void setExceptionContext(final SBonitaException sBonitaException, final Map context) + protected void setExceptionContext(final ExceptionContext sBonitaException, final Map context) throws SBonitaException { if (rootProcessInstanceId < 0) { final ServiceAccessor serviceAccessor = getServiceAccessor(context); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/TxInHandleFailureWrappingWork.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/TxInHandleFailureWrappingWork.java index c7a069faf4f..ced68f8a018 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/TxInHandleFailureWrappingWork.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/TxInHandleFailureWrappingWork.java @@ -17,6 +17,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; +import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.execution.work.WrappingBonitaWork; import org.bonitasoft.engine.service.ServiceAccessor; @@ -31,7 +32,7 @@ */ public abstract class TxInHandleFailureWrappingWork extends WrappingBonitaWork { - public TxInHandleFailureWrappingWork(final BonitaWork work) { + protected TxInHandleFailureWrappingWork(final BonitaWork work) { super(work); } @@ -43,18 +44,17 @@ public CompletableFuture work(final Map context) throws Ex @Override public void handleFailure(final Throwable e, final Map context) throws Exception { // Enrich the exception before log it. - if (e instanceof SBonitaException) { + if (e instanceof ExceptionContext exceptionContext) { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); transactionService.executeInTransaction((Callable) () -> { - setExceptionContext((SBonitaException) e, context); - + setExceptionContext(exceptionContext, context); return null; }); } getWrappedWork().handleFailure(e, context); } - protected abstract void setExceptionContext(final SBonitaException sBonitaException, + protected abstract void setExceptionContext(final ExceptionContext exceptionContext, final Map context) throws SBonitaException; } diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ServiceAccessor.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ServiceAccessor.java index c099edbe902..7f8edcafed3 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ServiceAccessor.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ServiceAccessor.java @@ -45,7 +45,7 @@ import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; -import org.bonitasoft.engine.core.process.instance.api.BpmFailureService; +import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService; @@ -135,7 +135,7 @@ public interface ServiceAccessor { ActivityInstanceService getActivityInstanceService(); - BpmFailureService getBpmFailureService(); + BPMFailureService getBpmFailureService(); BPMInstancesCreator getBPMInstancesCreator(); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringServiceAccessor.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringServiceAccessor.java index 528749679e9..2c4ec1a4278 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringServiceAccessor.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringServiceAccessor.java @@ -46,7 +46,7 @@ import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; -import org.bonitasoft.engine.core.process.instance.api.BpmFailureService; +import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService; @@ -192,8 +192,8 @@ public ActivityInstanceService getActivityInstanceService() { } @Override - public BpmFailureService getBpmFailureService() { - return beanAccessor.getService(BpmFailureService.class); + public BPMFailureService getBpmFailureService() { + return beanAccessor.getService(BPMFailureService.class); } @Override diff --git a/bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml b/bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml index 1ba21205ae2..31be3e9138c 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml +++ b/bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml @@ -651,7 +651,7 @@ - org.bonitasoft.engine.core.process.instance.model.SBpmFailure + org.bonitasoft.engine.core.process.instance.model.SBPMFailure @@ -660,7 +660,7 @@ - org.bonitasoft.engine.core.process.instance.model.SABpmFailure + org.bonitasoft.engine.core.process.instance.model.SABPMFailure @@ -1034,8 +1034,8 @@ org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance org.bonitasoft.engine.queriablelogger.model.SQueriableLog org.bonitasoft.engine.queriablelogger.model.SQueriableLogParameter - org.bonitasoft.engine.core.process.instance.model.SBpmFailure - org.bonitasoft.engine.core.process.instance.model.SABpmFailure + org.bonitasoft.engine.core.process.instance.model.SBPMFailure + org.bonitasoft.engine.core.process.instance.model.SABPMFailure diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/StateBehaviorsTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/StateBehaviorsTest.java index 88034e6bd64..139a90b131c 100644 --- a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/StateBehaviorsTest.java +++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/StateBehaviorsTest.java @@ -16,10 +16,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bpm.connector.ConnectorState.TO_BE_EXECUTED; import static org.bonitasoft.engine.bpm.connector.ConnectorState.TO_RE_EXECUTE; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyMap; import java.util.Arrays; import java.util.Collections; @@ -90,6 +89,7 @@ public class StateBehaviorsTest { @Before public void setConstants() { when(flowNodeInstance.getId()).thenReturn(flownodeInstanceId); + when(flowNodeDefinition.getName()).thenReturn("Step"); } @Test @@ -177,7 +177,7 @@ public void should_mapUsingUserFilters_throw_exception_when_empty_return() throw anyMap(), nullable(ClassLoader.class), nullable(SExpressionContext.class), eq("actor")); expectedException.expect(SActivityStateExecutionException.class); - expectedException.expectMessage("no user id returned by the user filter"); + expectedException.expectMessage("No user id returned by the user filter userFilterDefinition on activity Step"); //when stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, "actor", processDefinitionId, userFilterDefinition); @@ -191,7 +191,7 @@ public void should_mapUsingUserFilters_throw_exception_when_null_return() throws anyMap(), nullable(ClassLoader.class), nullable(SExpressionContext.class), eq("actor")); expectedException.expect(SActivityStateExecutionException.class); - expectedException.expectMessage("no user id returned by the user filter"); + expectedException.expectMessage("No user id returned by the user filter userFilterDefinition on activity Step"); //when stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, "actor", processDefinitionId, userFilterDefinition); @@ -205,7 +205,7 @@ public void should_mapUsingUserFilters_throw_exception_when_return_contains_minu anyMap(), nullable(ClassLoader.class), nullable(SExpressionContext.class), eq("actor")); expectedException.expect(SActivityStateExecutionException.class); - expectedException.expectMessage("no user id returned by the user filter"); + expectedException.expectMessage("No user id returned by the user filter userFilterDefinition on activity Step"); //when stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, "actor", processDefinitionId, userFilterDefinition); @@ -219,7 +219,7 @@ public void should_mapUsingUserFilters_throw_exception_when_return_contains_0() anyMap(), nullable(ClassLoader.class), nullable(SExpressionContext.class), eq("actor")); expectedException.expect(SActivityStateExecutionException.class); - expectedException.expectMessage("no user id returned by the user filter"); + expectedException.expectMessage("No user id returned by the user filter userFilterDefinition on activity Step"); //when stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, "actor", processDefinitionId, userFilterDefinition); diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluatorTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluatorTest.java index 70a3d23b35e..05b897c8ae6 100644 --- a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluatorTest.java +++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluatorTest.java @@ -14,7 +14,7 @@ package org.bonitasoft.engine.execution.transition; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; @@ -75,7 +75,7 @@ public void evaluateCondition_should_return_null_when_there_is_no_condition() th } @Test - public void evaluateCondition_should_throw_SExpressionEvaluationException_when_expression_return_type_is_not_a_boolean() + public void evaluateCondition_should_throw_STransitionConditionEvaluationException_when_expression_return_type_is_not_a_boolean() throws Exception { //given SExpression condition = mock(SExpression.class); @@ -84,19 +84,17 @@ public void evaluateCondition_should_throw_SExpressionEvaluationException_when_e STransitionDefinitionImpl transition = new STransitionDefinitionImpl("t1"); transition.setCondition(condition); - try { - //when + var exception = assertThrows(STransitionConditionEvaluationException.class, () -> { evaluator.evaluateCondition(transition, context); - fail("Exception expected"); - } catch (SExpressionEvaluationException e) { - //then - assertThat(e.getMessage()) - .isEqualTo("Condition expression must return a boolean, on transition: " + transition.getName()); - assertThat(e.getExpressionName()).isEqualTo("isTrue"); + }); - verifyNoInteractions(expressionResolverService); - } + //then + assertThat(exception).hasMessage("TRANSITION_NAME=t1 | Condition expression must return a boolean"); + assertThat(exception.getCause()).isInstanceOf(SExpressionEvaluationException.class); + var cause = (SExpressionEvaluationException) exception.getCause(); + assertThat(cause.getExpressionName()).isEqualTo("isTrue"); + verifyNoInteractions(expressionResolverService); } private SExpression buildBooleanExpression(boolean value) { diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/BPMWorkFactoryTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/BPMWorkFactoryTest.java index 462080acd3a..b4ec206cc09 100644 --- a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/BPMWorkFactoryTest.java +++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/BPMWorkFactoryTest.java @@ -116,7 +116,8 @@ public void createExecuteFlowNode() { @Test public void createExecuteConnectorOfProcess() { final WrappingBonitaWork work = (WrappingBonitaWork) workFactory.create(workFactory - .createExecuteConnectorOfProcessDescriptor(1L, 2L, 4L, 3L, "connectorDefName", ConnectorEvent.ON_ENTER, + .createExecuteConnectorOfProcessDescriptor(1L, 2L, 4L, 3L, "groovy", "connectorDefName", + ConnectorEvent.ON_ENTER, null)); Assert.assertTrue("A ProcessDefinitionContextWork is missing", containsFailureHandlingProcessDefinition(work)); } @@ -136,7 +137,8 @@ public void createExecuteConnectorOfProcessWithFlowNodeSelector() { new FlowNodeNameFilter(Arrays.asList("start1", "start2"))); final WrappingBonitaWork work = (WrappingBonitaWork) workFactory.create(workFactory - .createExecuteConnectorOfProcessDescriptor(1L, 2L, 4L, 3L, "connectorDefName", ConnectorEvent.ON_ENTER, + .createExecuteConnectorOfProcessDescriptor(1L, 2L, 4L, 3L, "groovy", "connectorDefName", + ConnectorEvent.ON_ENTER, flowNodeSelector)); assertThat(getWorkOfClass(work, ExecuteConnectorOfProcess.class).filterFlowNodeDefinitions.mustSelect(start1)) @@ -158,7 +160,8 @@ public void should_be_able_to_create_execute_connector_work_with_empty_flownode_ new FlowNodeNameFilter(emptyList())); final WrappingBonitaWork work = (WrappingBonitaWork) workFactory.create(workFactory - .createExecuteConnectorOfProcessDescriptor(1L, 2L, 4L, 3L, "connectorDefName", ConnectorEvent.ON_ENTER, + .createExecuteConnectorOfProcessDescriptor(1L, 2L, 4L, 3L, "groovy", "connectorDefName", + ConnectorEvent.ON_ENTER, flowNodeSelector)); assertThat(getWorkOfClass(work, ExecuteConnectorOfProcess.class).filterFlowNodeDefinitions.mustSelect(start1)) @@ -168,7 +171,10 @@ public void should_be_able_to_create_execute_connector_work_with_empty_flownode_ @Test public void createExecuteConnectorOfActivity() { final WrappingBonitaWork work = (WrappingBonitaWork) workFactory - .create(workFactory.createExecuteConnectorOfActivityDescriptor(1L, 3L, 4L, 5L, 6, "connectorDefName")); + .create(workFactory.createExecuteConnectorOfActivityDescriptor(1L, 3L, 4L, 5L, 6, + "groovy", + "connectorDefName", + "ON_ENTER")); Assert.assertTrue("A ProcessDefinitionContextWork is missing", containsFailureHandlingProcessDefinition(work)); Assert.assertTrue("A ProcessInstanceContextWork is missing", containsFailureHandlingProcessInstance(work)); diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWorkTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWorkTest.java index 88ea3df0795..25483ed9f70 100644 --- a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWorkTest.java +++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWorkTest.java @@ -106,7 +106,7 @@ protected SConnectorDefinition getSConnectorDefinition(ProcessDefinitionService } @Override - protected void setContainerInFail(Map context) throws SBonitaException { + protected void setContainerInFail(Map context, Throwable t) throws SBonitaException { } @Override diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/SetInFailCallableTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/SetInFailCallableTest.java index e42e6b7fcd5..7f697fabca8 100644 --- a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/SetInFailCallableTest.java +++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/SetInFailCallableTest.java @@ -16,8 +16,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.bonitasoft.engine.core.process.instance.api.BpmFailureService; -import org.bonitasoft.engine.core.process.instance.api.exceptions.FailureContext; +import org.bonitasoft.engine.commons.exceptions.ScopedException; +import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.junit.Before; import org.junit.Test; @@ -35,20 +35,19 @@ public class SetInFailCallableTest { private SFlowNodeInstance flowNodeInstance; @Mock - private BpmFailureService failureService; - - @Mock - private FailureContext failureContext; + private BPMFailureService failureService; public static final long PROCESS_DEFINITION_ID = 25L; private static final long FLOW_NODE_INSTANCE_ID = 123L; private SetInFailCallable setInFailCallable; + private BPMFailureService.Failure failure; @Before public void setUp() throws Exception { when(flowNodeInstance.getId()).thenReturn(FLOW_NODE_INSTANCE_ID); - setInFailCallable = new SetInFailCallable(failedStateSetter, flowNodeInstance, failureService, failureContext); + failure = new BPMFailureService.Failure(ScopedException.UNKNOWN_SCOPE, new RuntimeException()); + setInFailCallable = new SetInFailCallable(failedStateSetter, flowNodeInstance, failureService, failure); } @Test @@ -61,4 +60,14 @@ public void call_should_setState() throws Exception { verify(failedStateSetter).setAsFailed(FLOW_NODE_INSTANCE_ID); } + @Test + public void call_should_create_failure() throws Exception { + + //when + setInFailCallable.call(); + + //then + verify(failureService).createFlowNodeFailure(flowNodeInstance, failure); + } + } diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWorkTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWorkTest.java index 8637c54924f..09203b0a022 100644 --- a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWorkTest.java +++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWorkTest.java @@ -38,12 +38,15 @@ public class ConnectorDefinitionAndInstanceContextWorkTest extends AbstractConte private static final ConnectorEvent ACTIVATION_EVENT = ConnectorEvent.ON_ENTER; private static final long CONNECTOR_INSTANCE_ID = 10L; + private static final String CONNECTOR_DEFINITION_ID = "groovy"; @Override @Before public void before() throws SBonitaException { - txBonitaWork = spy(new ConnectorDefinitionAndInstanceContextWork(wrappedWork, CONNECTOR_DEFINITION_NAME, - CONNECTOR_INSTANCE_ID)); + txBonitaWork = spy(new ConnectorDefinitionAndInstanceContextWork(wrappedWork, + CONNECTOR_DEFINITION_ID, + CONNECTOR_DEFINITION_NAME, + CONNECTOR_INSTANCE_ID, ConnectorEvent.ON_ENTER)); super.before(); } @@ -64,7 +67,9 @@ public void handleFailureWithNameAndId() throws Throwable { @Test public void handleFailureWithNameAndIdAndActivationEvent() throws Throwable { - txBonitaWork = spy(new ConnectorDefinitionAndInstanceContextWork(wrappedWork, CONNECTOR_DEFINITION_NAME, + txBonitaWork = spy(new ConnectorDefinitionAndInstanceContextWork(wrappedWork, + CONNECTOR_DEFINITION_ID, + CONNECTOR_DEFINITION_NAME, CONNECTOR_INSTANCE_ID, ACTIVATION_EVENT)); final Map context = singletonMap("serviceAccessor", serviceAccessor); diff --git a/bpm/bonita-core/bonita-process-instance/build.gradle b/bpm/bonita-core/bonita-process-instance/build.gradle index 2d34607b38b..012f828a657 100644 --- a/bpm/bonita-core/bonita-process-instance/build.gradle +++ b/bpm/bonita-core/bonita-process-instance/build.gradle @@ -23,11 +23,14 @@ dependencies { api project(':bpm:bonita-core:bonita-contract-data') api 'org.bonitasoft.engine:bonita-connector-model' + testImplementation libs.junit5api testImplementation libs.assertj - testImplementation libs.mockitoCore + testImplementation libs.mockitoJunitJupiter testImplementation libs.systemRules testRuntimeOnly libs.logback + testRuntimeOnly libs.junitJupiterEngine + testRuntimeOnly libs.junitVintageEngine annotationProcessor libs.lombok compileOnly libs.lombok diff --git a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorServiceImpl.java b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorServiceImpl.java index 0ba90d4f55d..135b1de644b 100644 --- a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorServiceImpl.java +++ b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorServiceImpl.java @@ -318,15 +318,20 @@ public Map evaluateInputParameters(final String connectorId, final Map inputParameters = new HashMap<>(parameters.size()); try { for (final Entry input : parameters.entrySet()) { - if (sExpressionContext != null) { - final String key = input.getKey(); - if (inputValues != null && !inputValues.isEmpty() && inputValues.containsKey(key)) { - sExpressionContext.setSerializableInputValues(inputValues.get(key)); + try { + if (sExpressionContext != null) { + final String key = input.getKey(); + if (inputValues != null && !inputValues.isEmpty() && inputValues.containsKey(key)) { + sExpressionContext.setSerializableInputValues(inputValues.get(key)); + } + inputParameters.put(input.getKey(), + expressionResolverService.evaluate(input.getValue(), sExpressionContext)); + } else { + inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue())); } - inputParameters.put(input.getKey(), - expressionResolverService.evaluate(input.getValue(), sExpressionContext)); - } else { - inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue())); + } catch (SBonitaException e) { + e.setConnectorInputOnContext(input.getKey()); + throw e; } } } finally { diff --git a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/BpmFailureService.java b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/BPMFailureService.java similarity index 79% rename from bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/BpmFailureService.java rename to bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/BPMFailureService.java index 238dbb39a88..db10a27a7a2 100644 --- a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/BpmFailureService.java +++ b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/BPMFailureService.java @@ -15,17 +15,17 @@ import java.util.List; -import org.bonitasoft.engine.core.process.instance.model.SBpmFailure; +import org.bonitasoft.engine.core.process.instance.model.SBPMFailure; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.services.SPersistenceException; -public interface BpmFailureService { +public interface BPMFailureService { - void createFlowNodeFailure(SFlowNodeInstance flowNodeInstance, + SBPMFailure createFlowNodeFailure(SFlowNodeInstance flowNodeInstance, Failure failure) throws SPersistenceException; - List getFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException; + List getFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException; - record Failure(String scope, String context, Throwable throwable){} + record Failure(String scope, Throwable throwable){} } diff --git a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityStateExecutionException.java b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityStateExecutionException.java index 617b6ee095c..9339fd4cf1a 100644 --- a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityStateExecutionException.java +++ b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityStateExecutionException.java @@ -33,4 +33,12 @@ public SActivityStateExecutionException(final String message, final Throwable ca super(message, cause); } + public SActivityStateExecutionException(final String message, String scope, final Throwable cause) { + super(message, scope, cause); + } + + public SActivityStateExecutionException(final String message, String scope) { + super(message, scope); + } + } diff --git a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeExecutionException.java b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeExecutionException.java index e28f5c72943..6153f8a7ada 100644 --- a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeExecutionException.java +++ b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeExecutionException.java @@ -14,7 +14,6 @@ package org.bonitasoft.engine.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; -import org.bonitasoft.engine.core.process.instance.api.BpmFailureService; /** * An unexpected error happened during the execution of the flow node @@ -22,10 +21,18 @@ * * @author Celine Souchet */ -public class SFlowNodeExecutionException extends SBonitaException implements FailureContext { +public class SFlowNodeExecutionException extends SBonitaException { private static final long serialVersionUID = 5549874111741638842L; + public SFlowNodeExecutionException(final String message, final String scope, final Throwable cause) { + super(message, scope, cause); + } + + public SFlowNodeExecutionException(final String message, final String scope) { + super(message, scope); + } + public SFlowNodeExecutionException(final String message, final Throwable cause) { super(message, cause); } @@ -38,8 +45,4 @@ public SFlowNodeExecutionException(final Throwable cause) { super(cause); } - @Override - public BpmFailureService.Failure createFailure() { - return new BpmFailureService.Failure(getFailureScope(), getFailureContext(), this); - } } diff --git a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImpl.java b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImpl.java new file mode 100644 index 00000000000..cbc98766a85 --- /dev/null +++ b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImpl.java @@ -0,0 +1,155 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.core.process.instance.impl; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.bonitasoft.engine.commons.exceptions.ExceptionContext; +import org.bonitasoft.engine.commons.exceptions.SExceptionContext; +import org.bonitasoft.engine.connector.ConnectorValidationException; +import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; +import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; +import org.bonitasoft.engine.core.process.instance.model.SBPMFailure; +import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; +import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; +import org.bonitasoft.engine.persistence.QueryOptions; +import org.bonitasoft.engine.persistence.SBonitaReadException; +import org.bonitasoft.engine.persistence.SelectListDescriptor; +import org.bonitasoft.engine.services.PersistenceService; +import org.bonitasoft.engine.services.SPersistenceException; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class BPMFailureServiceImpl implements BPMFailureService { + + private static final String TYPE_SEPARATOR = "::"; + private static final String CONTEXT_PATH_SEPARATOR = "//"; + + private final PersistenceService persistenceService; + + public BPMFailureServiceImpl(PersistenceService persistenceService) { + this.persistenceService = persistenceService; + } + + @Override + public SBPMFailure createFlowNodeFailure(SFlowNodeInstance flowNodeInstance, Failure failure) + throws SPersistenceException { + log.debug("Adding failure for flow node instance {}", flowNodeInstance.getId()); + var bpmFailure = SBPMFailure.builder() + .flowNodeInstanceId(flowNodeInstance.getId()) + .processInstanceId(flowNodeInstance.getParentProcessInstanceId()) + .processDefinitionId(flowNodeInstance.getProcessDefinitionId()) + .scope(failure.scope()) + .context(buildContext(failure.throwable())) + .errorMessage(ExceptionUtils.getRootCauseMessage(failure.throwable())) + .stackTrace(ExceptionUtils.getStackTrace(failure.throwable())) + .build(); + return persistenceService.insert(bpmFailure); + } + + @Override + public List getFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException { + final QueryOptions queryOptions = new QueryOptions(0, maxResults); + final Map parameters = Map.ofEntries(Map.entry("flowNodeInstanceId", flowNodeInstanceId)); + return persistenceService.selectList(new SelectListDescriptor<>("getFlowNodeFailures", parameters, + SBPMFailure.class, queryOptions)); + } + + private static String buildContext(Throwable e) { + Map ctx = new TreeMap<>(); + if (e instanceof ExceptionContext exceptionContext) { + ctx = exceptionContext.getContext(); + } + var contextBuilder = new StringBuilder(); + + if (ctx.containsKey(SExceptionContext.MESSAGE_INSTANCE_NAME)) { + contextBuilder.append("message") + .append(TYPE_SEPARATOR) + .append(ctx.get(SExceptionContext.MESSAGE_INSTANCE_NAME)); + } + + addConnectorContext(contextBuilder, e, ctx); + + addTransitionContext(contextBuilder, ctx); + + var evaluationException = ExceptionUtils.throwableOfType(e, SExpressionEvaluationException.class); + if (evaluationException != null) { + addContextPathSeparator(contextBuilder); + contextBuilder.append("expression") + .append(TYPE_SEPARATOR) + .append(evaluationException.getExpressionName()); + } + return contextBuilder.toString(); + } + + private static void addTransitionContext(StringBuilder contextBuilder, + Map ctx) { + if (ctx.containsKey(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME)) { + if (ctx.containsKey(SExceptionContext.TRANSITION_NAME)) { + contextBuilder.append(ctx.get(SExceptionContext.TRANSITION_NAME)); + addContextPathSeparator(contextBuilder); + } + contextBuilder.append("to") + .append(TYPE_SEPARATOR) + .append(ctx.get(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME)); + } + } + + private static void addConnectorContext(StringBuilder contextBuilder, Throwable e, + Map ctx) { + if (ctx.containsKey(SExceptionContext.CONNECTOR_NAME)) { + addContextPathSeparator(contextBuilder); + contextBuilder.append(ctx.get(SExceptionContext.CONNECTOR_NAME)); + if (ctx.containsKey(SExceptionContext.CONNECTOR_DEFINITION_ID)) { + contextBuilder.append(TYPE_SEPARATOR); + contextBuilder.append(ctx.get(SExceptionContext.CONNECTOR_DEFINITION_ID)); + } + if (ctx.containsKey(SExceptionContext.CONNECTOR_ACTIVATION_EVENT)) { + contextBuilder.append(TYPE_SEPARATOR); + contextBuilder.append(ctx.get(SExceptionContext.CONNECTOR_ACTIVATION_EVENT).toString().toLowerCase()); + } + var operationExecutionException = ExceptionUtils.throwableOfType(e, SOperationExecutionException.class); + if (operationExecutionException != null) { + addContextPathSeparator(contextBuilder); + contextBuilder.append("output"); + } + } + + if (ctx.containsKey(SExceptionContext.CONNECTOR_INPUT_NAME)) { + addContextPathSeparator(contextBuilder); + contextBuilder.append("input") + .append(TYPE_SEPARATOR) + .append(ctx.get(SExceptionContext.CONNECTOR_INPUT_NAME)); + } + + var connectorValidationEx = ExceptionUtils.throwableOfType(e, ConnectorValidationException.class); + if (connectorValidationEx != null) { + addContextPathSeparator(contextBuilder); + contextBuilder.append("input-validation"); + } + } + + private static void addContextPathSeparator(StringBuilder contextBuilder) { + if (!contextBuilder.isEmpty()) { + contextBuilder.append(CONTEXT_PATH_SEPARATOR); + } + } + +} diff --git a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/BpmFailureServiceImpl.java b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/BpmFailureServiceImpl.java deleted file mode 100644 index a3b1fc34b22..00000000000 --- a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/BpmFailureServiceImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (C) 2024 Bonitasoft S.A. - * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble - * This library is free software; you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation - * version 2.1 of the License. - * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * You should have received a copy of the GNU Lesser General Public License along with this - * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth - * Floor, Boston, MA 02110-1301, USA. - **/ -package org.bonitasoft.engine.core.process.instance.impl; - -import java.util.List; -import java.util.Map; - -import org.bonitasoft.engine.commons.ExceptionUtils; -import org.bonitasoft.engine.core.process.instance.api.BpmFailureService; -import org.bonitasoft.engine.core.process.instance.model.SBpmFailure; -import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; -import org.bonitasoft.engine.persistence.QueryOptions; -import org.bonitasoft.engine.persistence.SBonitaReadException; -import org.bonitasoft.engine.persistence.SelectListDescriptor; -import org.bonitasoft.engine.services.PersistenceService; -import org.bonitasoft.engine.services.SPersistenceException; -import org.springframework.stereotype.Service; - -@Service -public class BpmFailureServiceImpl implements BpmFailureService { - - private final PersistenceService persistenceService; - - public BpmFailureServiceImpl(PersistenceService persistenceService) { - this.persistenceService = persistenceService; - } - - @Override - public void createFlowNodeFailure(SFlowNodeInstance flowNodeInstance, Failure failure) - throws SPersistenceException { - persistenceService.insert(SBpmFailure.builder() - .flowNodeInstanceId(flowNodeInstance.getId()) - .processInstanceId(flowNodeInstance.getParentProcessInstanceId()) - .processDefinitionId(flowNodeInstance.getProcessDefinitionId()) - .scope(failure.scope()) - .context(failure.context()) - .errorMessage(ExceptionUtils.printRootCauseOnly(failure.throwable())) - .stackTrace(ExceptionUtils.printLightWeightStacktrace(failure.throwable(), 20)) - .build()); - } - - @Override - public List getFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException { - final QueryOptions queryOptions = new QueryOptions(0, maxResults); - final Map parameters = Map.ofEntries(Map.entry("flowNodeInstanceId", flowNodeInstanceId)); - return persistenceService.selectList(new SelectListDescriptor("getFlowNodeFailure", parameters, - SBpmFailure.class, queryOptions)); - } -} diff --git a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SABpmFailure.java b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SABPMFailure.java similarity index 93% rename from bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SABpmFailure.java rename to bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SABPMFailure.java index 0468a5f2524..e9d7e3e727f 100644 --- a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SABpmFailure.java +++ b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SABPMFailure.java @@ -31,7 +31,7 @@ @Builder @Entity @Table(name = "arch_bpm_failure") -public class SABpmFailure implements ArchivedPersistentObject { +public class SABPMFailure implements ArchivedPersistentObject { @Id private long id; @@ -47,7 +47,7 @@ public class SABpmFailure implements ArchivedPersistentObject { private long archiveDate; private long sourceObjectId; - public SABpmFailure(final SBpmFailure bpmFailure) { + public SABPMFailure(final SBPMFailure bpmFailure) { this.sourceObjectId = bpmFailure.getId(); this.processDefinitionId = bpmFailure.getProcessDefinitionId(); this.processInstanceId = bpmFailure.getProcessInstanceId(); @@ -60,7 +60,7 @@ public SABpmFailure(final SBpmFailure bpmFailure) { @Override public Class getPersistentObjectInterface() { - return SBpmFailure.class; + return SBPMFailure.class; } @Override diff --git a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SBpmFailure.java b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SBPMFailure.java similarity index 96% rename from bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SBpmFailure.java rename to bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SBPMFailure.java index 192c2ccc7a7..4f18eca7763 100644 --- a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SBpmFailure.java +++ b/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SBPMFailure.java @@ -32,7 +32,7 @@ @Builder @Entity @Table(name = "bpm_failure") -public class SBpmFailure implements PlatformPersistentObject { +public class SBPMFailure implements PlatformPersistentObject { @Id private long id; diff --git a/bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/failure/model/impl/hibernate/failure.queries.hbm.xml b/bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/failure/model/impl/hibernate/failure.queries.hbm.xml index df5213b6b59..811c2a24d40 100644 --- a/bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/failure/model/impl/hibernate/failure.queries.hbm.xml +++ b/bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/failure/model/impl/hibernate/failure.queries.hbm.xml @@ -2,11 +2,11 @@ - - SELECT f - FROM org.bonitasoft.engine.core.process.instance.model.SBpmFailure AS f - WHERE f.flowNodeInstanceId = :flowNodeInstanceId - ORDER BY f.failureDate DESC - + + SELECT f + FROM org.bonitasoft.engine.core.process.instance.model.SBPMFailure AS f + WHERE f.flowNodeInstanceId = :flowNodeInstanceId + ORDER BY f.failureDate DESC + diff --git a/bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImplTest.java b/bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImplTest.java new file mode 100644 index 00000000000..c812ef9f092 --- /dev/null +++ b/bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImplTest.java @@ -0,0 +1,232 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.core.process.instance.impl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.within; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.bonitasoft.engine.bpm.connector.ConnectorEvent; +import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; +import org.bonitasoft.engine.commons.exceptions.SExceptionContext; +import org.bonitasoft.engine.connector.ConnectorValidationException; +import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; +import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; +import org.bonitasoft.engine.core.process.instance.model.SBPMFailure; +import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; +import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; +import org.bonitasoft.engine.persistence.SelectListDescriptor; +import org.bonitasoft.engine.services.PersistenceService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.AdditionalAnswers; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class BPMFailureServiceImplTest { + + @Mock + private PersistenceService persistenceService; + + private BPMFailureServiceImpl service; + + @BeforeEach + void setup() throws Exception { + service = new BPMFailureServiceImpl(persistenceService); + when(persistenceService.insert(any(SBPMFailure.class))).thenAnswer(AdditionalAnswers.returnsFirstArg()); + } + + @Test + void createFlowNodeFailure() throws Exception { + var now = Instant.now(); + var flowNodeInstance = createFlowNodeInstance(); + var failure = new BPMFailureService.Failure("scope", new Throwable("error message")); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + verify(persistenceService).insert(bpmFailure); + assertThat(bpmFailure.getFlowNodeInstanceId()).isEqualTo(flowNodeInstance.getId()); + assertThat(bpmFailure.getProcessDefinitionId()).isEqualTo(flowNodeInstance.getProcessDefinitionId()); + assertThat(bpmFailure.getProcessInstanceId()).isEqualTo(flowNodeInstance.getParentProcessInstanceId()); + assertThat(bpmFailure.getScope()).isEqualTo("scope"); + assertThat(bpmFailure.getErrorMessage()).isEqualTo("Throwable: error message"); + assertThat(bpmFailure.getStackTrace()).isEqualTo(ExceptionUtils.getStackTrace(failure.throwable())); + assertThat(Instant.ofEpochMilli(bpmFailure.getFailureDate())).isCloseTo(now, within(1000, ChronoUnit.MILLIS)); + } + + private static SFlowNodeInstance createFlowNodeInstance() { + SFlowNodeInstance flowNodeInstance = Mockito.mock(SFlowNodeInstance.class); + when(flowNodeInstance.getId()).thenReturn(1L); + when(flowNodeInstance.getParentProcessInstanceId()).thenReturn(2L); + when(flowNodeInstance.getProcessDefinitionId()).thenReturn(3L); + return flowNodeInstance; + } + + @Test + void failureWithExpressionContext() throws Exception { + var flowNodeInstance = createFlowNodeInstance(); + var failure = new BPMFailureService.Failure("scope", + new SExpressionEvaluationException(new RuntimeException("error in expression"), "expressionName")); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + assertThat(bpmFailure.getContext()).isEqualTo("expression::expressionName"); + } + + @Test + void failureWithMessageContext() throws Exception { + var flowNodeInstance = createFlowNodeInstance(); + var errorInMessage = new SBonitaRuntimeException("error in message"); + errorInMessage.setMessageInstanceNameOnContext("messageName"); + var failure = new BPMFailureService.Failure("scope", errorInMessage); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + assertThat(bpmFailure.getContext()).isEqualTo("message::messageName"); + } + + @Test + void failureWithConnectorContext() throws Exception { + var flowNodeInstance = createFlowNodeInstance(); + var errorInConnector = new SBonitaRuntimeException("error in connector"); + errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); + errorInConnector.setConnectorNameOnContext("get-info"); + errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); + var failure = new BPMFailureService.Failure("scope", errorInConnector); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + assertThat(bpmFailure.getContext()).isEqualTo("get-info::rest-connector::on_enter"); + } + + @Test + void failureWithConnectorInputContext() throws Exception { + var flowNodeInstance = createFlowNodeInstance(); + var errorInConnector = new SBonitaRuntimeException("error in connector"); + errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); + errorInConnector.setConnectorNameOnContext("get-info"); + errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); + errorInConnector.setConnectorInputOnContext("username"); + var failure = new BPMFailureService.Failure("scope", errorInConnector); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + assertThat(bpmFailure.getContext()).isEqualTo("get-info::rest-connector::on_enter//input::username"); + } + + @Test + void failureWithConnectorInputExpressionContext() throws Exception { + var flowNodeInstance = createFlowNodeInstance(); + var errorInConnector = new SBonitaRuntimeException(new SExpressionEvaluationException( + new RuntimeException("error in input expression"), "expressionName")); + errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); + errorInConnector.setConnectorNameOnContext("get-info"); + errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); + errorInConnector.setConnectorInputOnContext("username"); + var failure = new BPMFailureService.Failure("scope", errorInConnector); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + assertThat(bpmFailure.getContext()) + .isEqualTo("get-info::rest-connector::on_enter//input::username//expression::expressionName"); + } + + @Test + void failureWithConnectorOutputExpressionContext() throws Exception { + var flowNodeInstance = createFlowNodeInstance(); + var errorInConnector = new SBonitaRuntimeException( + new SOperationExecutionException(new SExpressionEvaluationException( + new RuntimeException("error in output expression"), "expressionName"))); + errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); + errorInConnector.setConnectorNameOnContext("get-info"); + errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); + var failure = new BPMFailureService.Failure("scope", errorInConnector); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + assertThat(bpmFailure.getContext()) + .isEqualTo("get-info::rest-connector::on_enter//output//expression::expressionName"); + } + + @Test + void failureWithConnectorValidationContext() throws Exception { + var flowNodeInstance = createFlowNodeInstance(); + var errorInConnector = new SBonitaRuntimeException(new ConnectorValidationException("error in validation")); + errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); + errorInConnector.setConnectorNameOnContext("get-info"); + errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); + var failure = new BPMFailureService.Failure("scope", errorInConnector); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + assertThat(bpmFailure.getContext()).isEqualTo("get-info::rest-connector::on_enter//input-validation"); + } + + @Test + void failureWithNamedTransitionContext() throws Exception { + var flowNodeInstance = createFlowNodeInstance(); + var errorInTransition = new SBonitaRuntimeException(new SExpressionEvaluationException( + new RuntimeException("error in consition expression"), "expressionName")); + errorInTransition.getContext().put(SExceptionContext.TRANSITION_NAME, "transitionName"); + errorInTransition.getContext().put(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME, "gateway::gatewayName"); + var failure = new BPMFailureService.Failure("scope", errorInTransition); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + assertThat(bpmFailure.getContext()) + .isEqualTo("transitionName//to::gateway::gatewayName//expression::expressionName"); + } + + @Test + void failureWithUnnamedTransitionContext() throws Exception { + var flowNodeInstance = createFlowNodeInstance(); + var errorInTransition = new SBonitaRuntimeException(new SExpressionEvaluationException( + new RuntimeException("error in consition expression"), "expressionName")); + errorInTransition.getContext().put(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME, "gateway::gatewayName"); + var failure = new BPMFailureService.Failure("scope", errorInTransition); + + var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); + + assertThat(bpmFailure.getContext()) + .isEqualTo("to::gateway::gatewayName//expression::expressionName"); + } + + @Test + void getFlowNodeFailures() throws Exception { + service.getFlowNodeFailures(1, 10); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(SelectListDescriptor.class); + verify(persistenceService).selectList(captor.capture()); + + var descriptor = captor.getValue(); + assertThat(descriptor.getQueryName()).isEqualTo("getFlowNodeFailures"); + assertThat(descriptor.getInputParameter("flowNodeInstanceId")).isEqualTo(1L); + assertThat(descriptor.getReturnType()).isEqualTo(SBPMFailure.class); + assertThat(descriptor.getStartIndex()).isZero(); + assertThat(descriptor.getPageSize()).isEqualTo(10); + } +} diff --git a/bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/UserFilterServiceImpl.java b/bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/UserFilterServiceImpl.java index cf1d453f110..67a99ef788b 100644 --- a/bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/UserFilterServiceImpl.java +++ b/bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/UserFilterServiceImpl.java @@ -24,6 +24,7 @@ import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; +import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.connector.ConnectorExecutor; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; @@ -116,7 +117,7 @@ public FilterResult executeFilter(final long processDefinitionId, final SUserFil } } catch (final SUserFilterExecutionException e) { throw e; - } catch (final Throwable e) {// catch throwable because we might have NoClassDefFound... see ENGINE-1333 + } catch (final Throwable e) { // catch throwable because we might have NoClassDefFound... see ENGINE-1333 throw new SUserFilterExecutionException(e); } @@ -196,11 +197,16 @@ protected FilterResult executeFilterInClassloader(final String implementationCla final SConnectorUserFilterAdapter adapter = new SConnectorUserFilterAdapter(filter, actorName); final HashMap inputParameters = new HashMap<>(parameters.size()); for (final Entry input : parameters.entrySet()) { - if (expressionContext != null) { - inputParameters.put(input.getKey(), - expressionResolverService.evaluate(input.getValue(), expressionContext)); - } else { - inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue())); + try { + if (expressionContext != null) { + inputParameters.put(input.getKey(), + expressionResolverService.evaluate(input.getValue(), expressionContext)); + } else { + inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue())); + } + } catch (SBonitaException e) { + e.setConnectorInputOnContext(input.getKey()); + throw e; } } connectorExecutor.execute(adapter, inputParameters, classLoader).get(); diff --git a/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ExceptionContext.java b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ExceptionContext.java new file mode 100644 index 00000000000..51455900c94 --- /dev/null +++ b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ExceptionContext.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.commons.exceptions; + +import java.io.Serializable; +import java.util.Map; + +public interface ExceptionContext { + + Map getContext(); + + void setProcessDefinitionIdOnContext(final long id); + + void setProcessDefinitionNameOnContext(final String name); + + void setProcessDefinitionVersionOnContext(final String version); + + void setProcessInstanceIdOnContext(final long id); + + void setRootProcessInstanceIdOnContext(final long id); + + void setConnectorDefinitionIdOnContext(final String id); + + void setConnectorInputOnContext(String inputName); + + void setConnectorNameOnContext(String name); + + void setConnectorImplementationClassNameOnContext(final String name); + + void setConnectorDefinitionVersionOnContext(final String version); + + void setConnectorActivationEventOnContext(final String activationEvent); + + void setConnectorInstanceIdOnContext(final long id); + + void setFlowNodeDefinitionIdOnContext(final long id); + + void setFlowNodeInstanceIdOnContext(final long id); + + void setFlowNodeNameOnContext(final String name); + + void setMessageInstanceNameOnContext(final String name); + + void setMessageInstanceTargetProcessOnContext(final String name); + + void setMessageInstanceTargetFlowNodeOnContext(final String name); + + void setWaitingMessageEventTypeOnContext(final String eventType); + + void setDocumentIdOnContext(final long id); + + void setUserIdOnContext(final long userId); + + void setGroupIdOnContext(final long groupId); + + void setRoleIdOnContext(final long roleId); + +} diff --git a/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaException.java b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaException.java index 8056186324a..1c20f1fb371 100644 --- a/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaException.java +++ b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaException.java @@ -23,7 +23,7 @@ * @author Matthieu Chaffotte * @author Celine Souchet */ -public abstract class SBonitaException extends Exception { +public abstract class SBonitaException extends Exception implements ExceptionContext, ScopedException { private static final long serialVersionUID = -500856379312027085L; @@ -31,19 +31,21 @@ public abstract class SBonitaException extends Exception { private final Object[] arguments; + private String scope = ScopedException.UNKNOWN_SCOPE; + private final Map context = new TreeMap(); /** * Default constructor */ - public SBonitaException() { + protected SBonitaException() { this((Object[]) null); } /** * @param arguments */ - public SBonitaException(final Object... arguments) { + protected SBonitaException(final Object... arguments) { super(); exceptionId = this.getClass().getName(); this.arguments = arguments; @@ -52,37 +54,78 @@ public SBonitaException(final Object... arguments) { /** * @param message */ - public SBonitaException(final String message) { + protected SBonitaException(final String message) { super(message); exceptionId = this.getClass().getName(); arguments = null; } + protected SBonitaException(final String message, String scope) { + super(message); + this.scope = scope; + exceptionId = this.getClass().getName(); + arguments = null; + } + /** * @param message * @param cause */ - public SBonitaException(final String message, final Throwable cause) { + protected SBonitaException(final String message, final Throwable cause) { super(message, cause); exceptionId = this.getClass().getName(); arguments = null; + if (cause instanceof SBonitaException e && e.getContext() != null) { + this.context.putAll(e.getContext()); + } + if (cause instanceof ScopedException e) { + this.scope = e.getScope(); + } + } + + protected SBonitaException(final String message, String scope, final Throwable cause) { + super(message, cause); + exceptionId = this.getClass().getName(); + arguments = null; + this.scope = scope; + if (cause instanceof SBonitaException e && e.getContext() != null) { + this.context.putAll(e.getContext()); + } } /** * @param cause */ - public SBonitaException(final Throwable cause) { + protected SBonitaException(final Throwable cause) { this(cause, (Object[]) null); + if (cause instanceof ScopedException e) { + this.scope = e.getScope(); + } } /** * @param cause * @param arguments */ - public SBonitaException(final Throwable cause, final Object... arguments) { + protected SBonitaException(final Throwable cause, final Object... arguments) { super(cause); exceptionId = this.getClass().getName(); this.arguments = arguments; + if (cause instanceof SBonitaException e && e.getContext() != null) { + this.context.putAll(e.getContext()); + } + if (cause instanceof ScopedException e) { + this.scope = e.getScope(); + } + } + + @Override + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; } /** @@ -105,6 +148,7 @@ public Object[] getParameters() { * @return The context of the exception * @since 6.3 */ + @Override public Map getContext() { return context; } @@ -114,7 +158,8 @@ public Map getContext() { * The identifier of the process definition to set * @since 6.3 */ - public void setProcessDefinitionIdOnContext(final Long id) { + @Override + public void setProcessDefinitionIdOnContext(final long id) { context.put(SExceptionContext.PROCESS_DEFINITION_ID, id); } @@ -123,6 +168,7 @@ public void setProcessDefinitionIdOnContext(final Long id) { * The name of the process definition to set * @since 6.3 */ + @Override public void setProcessDefinitionNameOnContext(final String name) { context.put(SExceptionContext.PROCESS_NAME, name); } @@ -132,6 +178,7 @@ public void setProcessDefinitionNameOnContext(final String name) { * The version of the process definition to set * @since 6.3 */ + @Override public void setProcessDefinitionVersionOnContext(final String version) { context.put(SExceptionContext.PROCESS_VERSION, version); } @@ -141,7 +188,8 @@ public void setProcessDefinitionVersionOnContext(final String version) { * The identifier of the process instance to set * @since 6.3 */ - public void setProcessInstanceIdOnContext(final Long id) { + @Override + public void setProcessInstanceIdOnContext(final long id) { context.put(SExceptionContext.PROCESS_INSTANCE_ID, id); } @@ -150,7 +198,8 @@ public void setProcessInstanceIdOnContext(final Long id) { * The identifier of the root process instance to set * @since 6.3 */ - public void setRootProcessInstanceIdOnContext(final Long id) { + @Override + public void setRootProcessInstanceIdOnContext(final long id) { context.put(SExceptionContext.ROOT_PROCESS_INSTANCE_ID, id); } @@ -159,6 +208,7 @@ public void setRootProcessInstanceIdOnContext(final Long id) { * The identifier of the connector definition * @since 6.3 */ + @Override public void setConnectorDefinitionIdOnContext(final String id) { context.put(SExceptionContext.CONNECTOR_DEFINITION_ID, id); } @@ -168,6 +218,7 @@ public void setConnectorDefinitionIdOnContext(final String id) { * The class name of the implementation of the connector definition to set * @since 6.3 */ + @Override public void setConnectorImplementationClassNameOnContext(final String name) { context.put(SExceptionContext.CONNECTOR_IMPLEMENTATION_CLASS_NAME, name); } @@ -177,6 +228,7 @@ public void setConnectorImplementationClassNameOnContext(final String name) { * The version of the connector definition * @since 6.3 */ + @Override public void setConnectorDefinitionVersionOnContext(final String version) { context.put(SExceptionContext.CONNECTOR_DEFINITION_VERSION, version); } @@ -186,6 +238,7 @@ public void setConnectorDefinitionVersionOnContext(final String version) { * The event which activates the connector to set * @since 6.3 */ + @Override public void setConnectorActivationEventOnContext(final String activationEvent) { context.put(SExceptionContext.CONNECTOR_ACTIVATION_EVENT, activationEvent); } @@ -195,6 +248,7 @@ public void setConnectorActivationEventOnContext(final String activationEvent) { * The identifier of the connector instance to set * @since 6.3 */ + @Override public void setConnectorInstanceIdOnContext(final long id) { context.put(SExceptionContext.CONNECTOR_INSTANCE_ID, id); } @@ -204,6 +258,7 @@ public void setConnectorInstanceIdOnContext(final long id) { * The identifier of the flow node definition to set * @since 6.3 */ + @Override public void setFlowNodeDefinitionIdOnContext(final long id) { context.put(SExceptionContext.FLOW_NODE_DEFINITION_ID, id); } @@ -213,6 +268,7 @@ public void setFlowNodeDefinitionIdOnContext(final long id) { * The identifier of the flow node instance to set * @since 6.3 */ + @Override public void setFlowNodeInstanceIdOnContext(final long id) { context.put(SExceptionContext.FLOW_NODE_INSTANCE_ID, id); } @@ -222,6 +278,7 @@ public void setFlowNodeInstanceIdOnContext(final long id) { * The name of the flow node to set * @since 6.3 */ + @Override public void setFlowNodeNameOnContext(final String name) { context.put(SExceptionContext.FLOW_NODE_NAME, name); } @@ -231,6 +288,7 @@ public void setFlowNodeNameOnContext(final String name) { * The name of the message instance to set * @since 6.3 */ + @Override public void setMessageInstanceNameOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_NAME, name); } @@ -240,6 +298,7 @@ public void setMessageInstanceNameOnContext(final String name) { * The target process name of the message instance to set * @since 6.3 */ + @Override public void setMessageInstanceTargetProcessOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_PROCESS_NAME, name); } @@ -249,6 +308,7 @@ public void setMessageInstanceTargetProcessOnContext(final String name) { * The target flow node name of the message instance to set * @since 6.3 */ + @Override public void setMessageInstanceTargetFlowNodeOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, name); } @@ -258,6 +318,7 @@ public void setMessageInstanceTargetFlowNodeOnContext(final String name) { * The event type of the waiting message instance to set * @since 6.3 */ + @Override public void setWaitingMessageEventTypeOnContext(final String eventType) { context.put(SExceptionContext.WAITING_MESSAGE_INSTANCE_TYPE, eventType); } @@ -267,6 +328,7 @@ public void setWaitingMessageEventTypeOnContext(final String eventType) { * The identifier of the document * @since 6.3 */ + @Override public void setDocumentIdOnContext(final long id) { context.put(SExceptionContext.DOCUMENT_ID, id); } @@ -276,7 +338,8 @@ public void setDocumentIdOnContext(final long id) { * The identifier of the user * @since 6.3 */ - public void setUserIdOnContext(final Long userId) { + @Override + public void setUserIdOnContext(final long userId) { context.put(SExceptionContext.USER_ID, userId); } @@ -285,7 +348,8 @@ public void setUserIdOnContext(final Long userId) { * The identifier of the group * @since 6.3 */ - public void setGroupIdOnContext(final Long groupId) { + @Override + public void setGroupIdOnContext(final long groupId) { context.put(SExceptionContext.GROUP_ID, groupId); } @@ -294,10 +358,21 @@ public void setGroupIdOnContext(final Long groupId) { * The identifier of the role * @since 6.3 */ - public void setRoleIdOnContext(final Long roleId) { + @Override + public void setRoleIdOnContext(final long roleId) { context.put(SExceptionContext.ROLE_ID, roleId); } + @Override + public void setConnectorInputOnContext(String inputName) { + context.put(SExceptionContext.CONNECTOR_INPUT_NAME, inputName); + } + + @Override + public void setConnectorNameOnContext(String name) { + context.put(SExceptionContext.CONNECTOR_NAME, name); + } + @Override public String getMessage() { final StringBuilder stringBuilder = new StringBuilder(); @@ -311,7 +386,7 @@ private void appendCauseMessage(final StringBuilder stringBuilder) { if (message != null && message.isEmpty() && getCause() != null) { message = getCause().getMessage(); } - if (message != null && !message.trim().equals("")) { + if (message != null && !message.trim().isEmpty()) { stringBuilder.append(message); } } diff --git a/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaRuntimeException.java b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaRuntimeException.java index 65742d9a600..1b91bab28d2 100644 --- a/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaRuntimeException.java +++ b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaRuntimeException.java @@ -22,25 +22,48 @@ * @author Matthieu Chaffotte * @author Celine Souchet */ -public class SBonitaRuntimeException extends RuntimeException { +public class SBonitaRuntimeException extends RuntimeException implements ExceptionContext, ScopedException { private static final long serialVersionUID = 7268935639620676043L; - private final Map context; + private final Map context = new TreeMap<>(); + private String scope = ScopedException.UNKNOWN_SCOPE; public SBonitaRuntimeException(final Throwable cause) { super(cause); - context = new TreeMap(); + if (cause instanceof SBonitaException e && e.getContext() != null) { + this.context.putAll(e.getContext()); + } + if (cause instanceof ScopedException e) { + this.scope = e.getScope(); + } } public SBonitaRuntimeException(final String message, final Throwable cause) { super(message, cause); - context = new TreeMap(); + if (cause instanceof SBonitaException e && e.getContext() != null) { + this.context.putAll(e.getContext()); + } + if (cause instanceof ScopedException e) { + this.scope = e.getScope(); + } + } + + public SBonitaRuntimeException(final String message, String scope, final Throwable cause) { + super(message, cause); + this.scope = scope; + if (cause instanceof SBonitaException e && e.getContext() != null) { + this.context.putAll(e.getContext()); + } } public SBonitaRuntimeException(final String message) { super(message); - context = new TreeMap(); + } + + @Override + public String getScope() { + return this.scope; } /** @@ -56,7 +79,8 @@ public Map getContext() { * The identifier of the process definition to set * @since 6.3 */ - public void setProcessDefinitionIdOnContext(final Long id) { + @Override + public void setProcessDefinitionIdOnContext(final long id) { context.put(SExceptionContext.PROCESS_DEFINITION_ID, id); } @@ -65,6 +89,7 @@ public void setProcessDefinitionIdOnContext(final Long id) { * The name of the process definition to set * @since 6.3 */ + @Override public void setProcessDefinitionNameOnContext(final String name) { context.put(SExceptionContext.PROCESS_NAME, name); } @@ -74,6 +99,7 @@ public void setProcessDefinitionNameOnContext(final String name) { * The version of the process definition to set * @since 6.3 */ + @Override public void setProcessDefinitionVersionOnContext(final String version) { context.put(SExceptionContext.PROCESS_VERSION, version); } @@ -83,7 +109,8 @@ public void setProcessDefinitionVersionOnContext(final String version) { * The identifier of the process instance to set * @since 6.3 */ - public void setProcessInstanceIdOnContext(final Long id) { + @Override + public void setProcessInstanceIdOnContext(final long id) { context.put(SExceptionContext.PROCESS_INSTANCE_ID, id); } @@ -92,7 +119,8 @@ public void setProcessInstanceIdOnContext(final Long id) { * The identifier of the root process instance to set * @since 6.3 */ - public void setRootProcessInstanceIdOnContext(final Long id) { + @Override + public void setRootProcessInstanceIdOnContext(final long id) { context.put(SExceptionContext.ROOT_PROCESS_INSTANCE_ID, id); } @@ -101,16 +129,13 @@ public void setRootProcessInstanceIdOnContext(final Long id) { * The identifier of the connector definition * @since 6.3 */ + @Override public void setConnectorDefinitionIdOnContext(final String id) { context.put(SExceptionContext.CONNECTOR_DEFINITION_ID, id); } - /** - * @param name - * The class name of the implementation of the connector definition to set - * @since 6.3 - */ - public void setConnectorDefinitionImplementationClassNameOnContext(final String name) { + @Override + public void setConnectorImplementationClassNameOnContext(String name) { context.put(SExceptionContext.CONNECTOR_IMPLEMENTATION_CLASS_NAME, name); } @@ -119,6 +144,7 @@ public void setConnectorDefinitionImplementationClassNameOnContext(final String * The version of the connector definition * @since 6.3 */ + @Override public void setConnectorDefinitionVersionOnContext(final String version) { context.put(SExceptionContext.CONNECTOR_DEFINITION_VERSION, version); } @@ -128,6 +154,7 @@ public void setConnectorDefinitionVersionOnContext(final String version) { * The event which activates the connector to set * @since 6.3 */ + @Override public void setConnectorActivationEventOnContext(final String activationEvent) { context.put(SExceptionContext.CONNECTOR_ACTIVATION_EVENT, activationEvent); } @@ -137,6 +164,7 @@ public void setConnectorActivationEventOnContext(final String activationEvent) { * The identifier of the connector instance to set * @since 6.3 */ + @Override public void setConnectorInstanceIdOnContext(final long id) { context.put(SExceptionContext.CONNECTOR_INSTANCE_ID, id); } @@ -146,6 +174,7 @@ public void setConnectorInstanceIdOnContext(final long id) { * The identifier of the flow node definition to set * @since 6.3 */ + @Override public void setFlowNodeDefinitionIdOnContext(final long id) { context.put(SExceptionContext.FLOW_NODE_DEFINITION_ID, id); } @@ -155,6 +184,7 @@ public void setFlowNodeDefinitionIdOnContext(final long id) { * The identifier of the flow node instance to set * @since 6.3 */ + @Override public void setFlowNodeInstanceIdOnContext(final long id) { context.put(SExceptionContext.FLOW_NODE_INSTANCE_ID, id); } @@ -164,6 +194,7 @@ public void setFlowNodeInstanceIdOnContext(final long id) { * The name of the flow node to set * @since 6.3 */ + @Override public void setFlowNodeNameOnContext(final String name) { context.put(SExceptionContext.FLOW_NODE_NAME, name); } @@ -173,6 +204,7 @@ public void setFlowNodeNameOnContext(final String name) { * The name of the message instance to set * @since 6.3 */ + @Override public void setMessageInstanceNameOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_NAME, name); } @@ -182,6 +214,7 @@ public void setMessageInstanceNameOnContext(final String name) { * The target process name of the message instance to set * @since 6.3 */ + @Override public void setMessageInstanceTargetProcessOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_PROCESS_NAME, name); } @@ -191,6 +224,7 @@ public void setMessageInstanceTargetProcessOnContext(final String name) { * The target flow node name of the message instance to set * @since 6.3 */ + @Override public void setMessageInstanceTargetFlowNodeOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, name); } @@ -200,19 +234,41 @@ public void setMessageInstanceTargetFlowNodeOnContext(final String name) { * The event type of the waiting message instance to set * @since 6.3 */ + @Override public void setWaitingMessageEventTypeOnContext(final String eventType) { context.put(SExceptionContext.WAITING_MESSAGE_INSTANCE_TYPE, eventType); } - /** - * @param userId - * The identifier of the user - * @since 6.3 - */ - public void setUserIdOnContext(final long userId) { + @Override + public void setDocumentIdOnContext(long id) { + context.put(SExceptionContext.DOCUMENT_ID, id); + } + + @Override + public void setUserIdOnContext(long userId) { context.put(SExceptionContext.USER_ID, userId); } + @Override + public void setGroupIdOnContext(long groupId) { + context.put(SExceptionContext.GROUP_ID, groupId); + } + + @Override + public void setRoleIdOnContext(long roleId) { + context.put(SExceptionContext.ROLE_ID, roleId); + } + + @Override + public void setConnectorInputOnContext(String inputName) { + context.put(SExceptionContext.CONNECTOR_INPUT_NAME, inputName); + } + + @Override + public void setConnectorNameOnContext(String name) { + context.put(SExceptionContext.CONNECTOR_NAME, name); + } + @Override public String getMessage() { final StringBuilder stringBuilder = new StringBuilder(); @@ -226,7 +282,7 @@ private void appendCauseMessage(final StringBuilder stringBuilder) { if (message != null && message.isEmpty() && getCause() != null) { message = getCause().getMessage(); } - if (message != null && !message.trim().equals("")) { + if (message != null && !message.trim().isEmpty()) { stringBuilder.append(message); } } @@ -234,7 +290,10 @@ private void appendCauseMessage(final StringBuilder stringBuilder) { private void appendContextMessage(final StringBuilder stringBuilder) { if (!context.isEmpty()) { for (final Entry entry : context.entrySet()) { - stringBuilder.append(entry.getKey() + "=" + entry.getValue() + " | "); + stringBuilder.append(entry.getKey()); + stringBuilder.append("="); + stringBuilder.append(entry.getValue()); + stringBuilder.append(" | "); } } } diff --git a/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SExceptionContext.java b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SExceptionContext.java index f92969d19ee..8199ed24197 100644 --- a/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SExceptionContext.java +++ b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SExceptionContext.java @@ -67,10 +67,18 @@ public enum SExceptionContext { * Corresponding to the event type of the Waiting Message Instance */ WAITING_MESSAGE_INSTANCE_TYPE, + /** + * Corresponding to the name of the connector + */ + CONNECTOR_NAME, /** * Corresponding to the identifier of the connector definition */ CONNECTOR_DEFINITION_ID, + /** + * Corresponding to the name of the input of the connector + */ + CONNECTOR_INPUT_NAME, /** * Corresponding to the class name of the implementation of the connector definition */ @@ -102,6 +110,14 @@ public enum SExceptionContext { /** * Corresponding to the identifier of the document */ - DOCUMENT_ID; + DOCUMENT_ID, + /** + * Corresponding to the name of the transition + */ + TRANSITION_NAME, + /** + * Corresponding to the target flownode name of the transition + */ + TRANSITION_TARGET_FLOWNODE_NAME; } diff --git a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/FailureContext.java b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ScopedException.java similarity index 52% rename from bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/FailureContext.java rename to services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ScopedException.java index 5a6e172fa4c..43b6e6cdead 100644 --- a/bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/FailureContext.java +++ b/services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ScopedException.java @@ -11,34 +11,27 @@ * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301, USA. **/ -package org.bonitasoft.engine.core.process.instance.api.exceptions; +package org.bonitasoft.engine.commons.exceptions; -import org.bonitasoft.engine.core.process.instance.api.BpmFailureService; +public interface ScopedException { -public interface FailureContext { - - static final String UNKNOWN_SCOPE = "UNKNOWN"; - static final String EMPTY_CONTEXT = ""; + String UNKNOWN_SCOPE = "UNKNOWN"; + String GENERAL_INFORMATION = "General information"; + String OPERATION = "Operation"; + String EVENT = "Event"; + String ITERATION = "Iteration"; + String CONNECTOR = "Connector"; + String DATA = "Data initialization"; + String ACTOR_MAPPING = "Actor mapping"; + String OUTGOING_TRANSITION = "Outgoing transition"; /** - * Describe the scope of the failure with a human-readable failure category. + * Describe the scope of the exception with a human-readable category. * - * @return The scope of the failure + * @return The scope of the exception */ - default String getFailureScope() { + default String getScope() { return UNKNOWN_SCOPE; } - /** - * Additional context information about the failure. The returned string is formatted using - * the following pattern: "context1//context2//context3" - * - * @return The context of the failure - */ - default String getFailureContext() { - return EMPTY_CONTEXT; - } - - BpmFailureService.Failure createFailure(); - } diff --git a/services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernatePersistenceService.java b/services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernatePersistenceService.java index d7ccc92a6ae..c6d6a6dfbd1 100644 --- a/services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernatePersistenceService.java +++ b/services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernatePersistenceService.java @@ -140,7 +140,7 @@ public void flushStatements() throws SPersistenceException { } @Override - public void delete(final PersistentObject entity) throws SPersistenceException { + public void delete(final T entity) throws SPersistenceException { if (getLogger().isDebugEnabled()) { getLogger().debug( "Deleting instance of class " + entity.getClass().getSimpleName() + " with id=" + entity.getId()); @@ -199,7 +199,7 @@ public void deleteAll(final Class entityClass) throw } @Override - public void insert(final PersistentObject entity) throws SPersistenceException { + public T insert(final T entity) throws SPersistenceException { if (!(entity instanceof PlatformPersistentObject)) { setTenant(entity); } @@ -209,6 +209,7 @@ public void insert(final PersistentObject entity) throws SPersistenceException { setId(entity); try { session.save(entity); + return entity; } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) { throw new SRetryableException(e); } catch (final HibernateException he) { @@ -217,21 +218,20 @@ public void insert(final PersistentObject entity) throws SPersistenceException { } @Override - public void insertInBatch(final List entities) throws SPersistenceException { - for (final PersistentObject entity : entities) { - if (!(entity instanceof PlatformPersistentObject)) { - setTenant(entity); - } - } + public List insertInBatch(final List entities) throws SPersistenceException { if (!entities.isEmpty()) { final Session session = getSession(); for (final PersistentObject entity : entities) { + if (!(entity instanceof PlatformPersistentObject)) { + setTenant(entity); + } final Class entityClass = entity.getClass(); checkClassMapping(entityClass); setId(entity); session.save(entity); } } + return entities; } @Override diff --git a/services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/impl/RecorderImpl.java b/services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/impl/RecorderImpl.java index 61f3545362a..8cda24190dc 100644 --- a/services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/impl/RecorderImpl.java +++ b/services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/impl/RecorderImpl.java @@ -53,10 +53,10 @@ public RecorderImpl(final PersistenceService persistenceService, } @Override - public void recordInsert(final InsertRecord record, String type) throws SRecorderException { + public void recordInsert(final InsertRecord insertRecord, String type) throws SRecorderException { try { - persistenceService.insert(record.getEntity()); - eventService.fireEvent(createInsertEvent(record.getEntity(), type)); + var entity = persistenceService.insert(insertRecord.getEntity()); + eventService.fireEvent(createInsertEvent(entity, type)); } catch (final Exception e) { logExceptionsFromHandlers(e); throw new SRecorderException(e); @@ -83,10 +83,10 @@ private SUpdateEvent createUpdateEvent(PersistentObject entity, Map 0) - eventService.fireEvent(createUpdateEvent(record.getEntity(), record.getFields(), type)); + eventService.fireEvent(createUpdateEvent(updateRecord.getEntity(), updateRecord.getFields(), type)); return updateCount; } catch (final Exception e) { logExceptionsFromHandlers(e); diff --git a/services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/PersistenceService.java b/services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/PersistenceService.java index d33d25582af..cde8f837810 100644 --- a/services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/PersistenceService.java +++ b/services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/PersistenceService.java @@ -34,32 +34,26 @@ public interface PersistenceService extends ReadPersistenceService { /** * Add a record into the table by given persistentObject. * - * @param entity - * @throws SPersistenceException * @since 6.0 */ - void insert(final PersistentObject entity) throws SPersistenceException; + T insert(final T entity) throws SPersistenceException; /** - * @param entities - * @throws SPersistenceException + * */ - void insertInBatch(final List entities) throws SPersistenceException; + List insertInBatch(final List entities) throws SPersistenceException; /** * Delete a record from the table by given persistentObject. * - * @param entity - * @throws SPersistenceException * @since 6.0 */ - void delete(final PersistentObject entity) throws SPersistenceException; + void delete(final T entity) throws SPersistenceException; /** * Delete all records belong to the given entity class from the table. * * @param entityClass The class which extends persistentObject - * @throws SPersistenceException * @since 6.0 */ void deleteAll(final Class entityClass) throws SPersistenceException; @@ -75,11 +69,6 @@ public interface PersistenceService extends ReadPersistenceService { /** * Executes a query update. - * - * @param updateQueryName - * @param inputParameters - * @return - * @throws SPersistenceException */ int update(String updateQueryName, Map inputParameters) throws SPersistenceException; @@ -88,15 +77,12 @@ public interface PersistenceService extends ReadPersistenceService { * * @param entityClass Entity class corresponding to the table to empty * @param filters Filters - * @throws SPersistenceException * @since 6.1 */ void deleteByTenant(Class entityClass, List filters) throws SPersistenceException; /** - * @param desc - * @throws SPersistenceException */ void update(final UpdateDescriptor desc) throws SPersistenceException; @@ -107,7 +93,6 @@ void deleteByTenant(Class entityClass, List entityClass) throws SPersistenceException; @@ -117,7 +102,6 @@ void deleteByTenant(Class entityClass, List ids, final Class entityClass) throws SPersistenceException; diff --git a/services/bonita-persistence/src/test/java/org/bonitasoft/engine/recorder/impl/RecorderImplTest.java b/services/bonita-persistence/src/test/java/org/bonitasoft/engine/recorder/impl/RecorderImplTest.java index 1b7bf24185b..5b089e2ee29 100644 --- a/services/bonita-persistence/src/test/java/org/bonitasoft/engine/recorder/impl/RecorderImplTest.java +++ b/services/bonita-persistence/src/test/java/org/bonitasoft/engine/recorder/impl/RecorderImplTest.java @@ -13,8 +13,10 @@ **/ package org.bonitasoft.engine.recorder.impl; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.util.Collections; import java.util.UUID; @@ -28,6 +30,7 @@ import org.bonitasoft.engine.services.PersistenceService; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.AdditionalAnswers; import org.mockito.ArgumentMatcher; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -48,6 +51,7 @@ public class RecorderImplTest { @Test public void should_fire_event_when_recording_an_insert() throws Exception { + when(persistenceService.insert(any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); MyPersistentObject entity = entity(); recorder.recordInsert(insertRecord(entity), "theEvent"); diff --git a/services/bonita-temporary-content/src/main/java/org/bonitasoft/engine/temporary/content/TemporaryContentServiceImpl.java b/services/bonita-temporary-content/src/main/java/org/bonitasoft/engine/temporary/content/TemporaryContentServiceImpl.java index c5ddf56e40d..2db6ac08b22 100644 --- a/services/bonita-temporary-content/src/main/java/org/bonitasoft/engine/temporary/content/TemporaryContentServiceImpl.java +++ b/services/bonita-temporary-content/src/main/java/org/bonitasoft/engine/temporary/content/TemporaryContentServiceImpl.java @@ -54,8 +54,8 @@ public TemporaryContentServiceImpl( public String add(String fileName, InputStream content, String mimeType) throws SRecorderException, IOException, SPersistenceException { Blob data = BlobProxy.generateProxy(content, content.available()); - STemporaryContent temporaryContent = new STemporaryContent(fileName, data, mimeType); - platformPersistenceService.insert(temporaryContent); + STemporaryContent temporaryContent = platformPersistenceService + .insert(new STemporaryContent(fileName, data, mimeType)); return temporaryContent.getKey(); }