use of io.camunda.zeebe.engine.processing.common.Failure in project zeebe by zeebe-io.
the class BpmnDecisionBehavior method evaluateDecision.
/**
* Evaluate a decision during the processing of a bpmn element.
*
* @param element the called decision of the current bpmn element
* @param context process instance-related data of the element that is executed
* @return either an evaluated decision's result or a failure
*/
public Either<Failure, DecisionEvaluationResult> evaluateDecision(final ExecutableCalledDecision element, final BpmnElementContext context) {
final var scopeKey = context.getElementInstanceKey();
final var decisionIdOrFailure = evalDecisionIdExpression(element, scopeKey);
if (decisionIdOrFailure.isLeft()) {
return Either.left(decisionIdOrFailure.getLeft());
}
final var decisionId = decisionIdOrFailure.get();
// todo(#8571): avoid parsing drg every time
final var decisionOrFailure = findDecisionById(decisionId);
final var resultOrFailure = decisionOrFailure.flatMap(this::findDrgByDecision).mapLeft(failure -> new Failure("Expected to evaluate decision '%s', but %s".formatted(decisionId, failure.getMessage()))).flatMap(drg -> parseDrg(drg.getResource())).mapLeft(f -> new Failure(f.getMessage(), ErrorType.CALLED_DECISION_ERROR, scopeKey)).flatMap(drg -> {
final var evaluationResult = evaluateDecisionInDrg(drg, decisionId, scopeKey);
final var decision = decisionOrFailure.get();
writeDecisionEvaluationEvent(decision, evaluationResult, context);
if (evaluationResult.isFailure()) {
metrics.increaseFailedEvaluatedDmnElements(evaluationResult.getEvaluatedDecisions().size());
return Either.left(new Failure(evaluationResult.getFailureMessage(), ErrorType.DECISION_EVALUATION_ERROR, scopeKey));
} else {
metrics.increaseSuccessfullyEvaluatedDmnElements(evaluationResult.getEvaluatedDecisions().size());
return Either.right(evaluationResult);
}
});
resultOrFailure.ifRight(result -> {
// The output mapping behavior determines what to do with the decision result. Since the
// output mapping may fail and raise an incident, we need to write the variable to a
// record. This is because we want to evaluate the decision on element activation, while
// the output mapping happens on element completion. We don't want to re-evaluate the
// decision for output mapping related incidents.
triggerProcessEventWithResultVariable(context, element.getResultVariable(), result);
});
return resultOrFailure;
}
use of io.camunda.zeebe.engine.processing.common.Failure in project zeebe by camunda.
the class BpmnResourceTransformer method transformResource.
@Override
public Either<Failure, Void> transformResource(final DeploymentResource resource, final DeploymentRecord deployment) {
final BpmnModelInstance definition = readProcessDefinition(resource);
final String validationError = validator.validate(definition);
if (validationError == null) {
// transform the model to avoid unexpected failures that are not covered by the validator
bpmnTransformer.transformDefinitions(definition);
return checkForDuplicateBpmnId(definition, resource, deployment).map(ok -> {
transformProcessResource(deployment, resource, definition);
return null;
});
} else {
final var failureMessage = String.format("'%s': %s", resource.getResourceName(), validationError);
return Either.left(new Failure(failureMessage));
}
}
use of io.camunda.zeebe.engine.processing.common.Failure in project zeebe by camunda.
the class DeploymentCreateProcessor method subscribeToTimerStartEventIfExists.
private void subscribeToTimerStartEventIfExists(final TypedStreamWriter streamWriter, final SideEffects sideEffects, final ProcessMetadata processMetadata, final List<ExecutableStartEvent> startEvents) {
for (final ExecutableCatchEventElement startEvent : startEvents) {
if (startEvent.isTimer()) {
// There are no variables when there is no process instance yet,
// we use a negative scope key to indicate this
final long scopeKey = -1L;
final Either<Failure, Timer> timerOrError = startEvent.getTimerFactory().apply(expressionProcessor, scopeKey);
if (timerOrError.isLeft()) {
// todo(#4323): deal with this exceptional case without throwing an exception
throw new EvaluationException(timerOrError.getLeft().getMessage());
}
catchEventBehavior.subscribeToTimerEvent(NO_ELEMENT_INSTANCE, NO_ELEMENT_INSTANCE, processMetadata.getKey(), startEvent.getId(), timerOrError.get(), streamWriter, sideEffects);
}
}
}
use of io.camunda.zeebe.engine.processing.common.Failure in project zeebe by zeebe-io.
the class TriggerTimerProcessor method rescheduleTimer.
private void rescheduleTimer(final TimerRecord record, final ExecutableCatchEvent event, final TypedCommandWriter writer, final Consumer<SideEffectProducer> sideEffects) {
final Either<Failure, Timer> timer = event.getTimerFactory().apply(expressionProcessor, record.getElementInstanceKey());
if (timer.isLeft()) {
final String message = String.format("Expected to reschedule repeating timer for element with id '%s', but an error occurred: %s", BufferUtil.bufferAsString(event.getId()), timer.getLeft().getMessage());
throw new IllegalStateException(message);
// todo(#4208): raise incident instead of throwing an exception
}
int repetitions = record.getRepetitions();
if (repetitions != RepeatingInterval.INFINITE) {
repetitions--;
}
final Interval interval = timer.map(Timer::getInterval).get();
final Timer repeatingInterval = new RepeatingInterval(repetitions, interval);
catchEventBehavior.subscribeToTimerEvent(record.getElementInstanceKey(), record.getProcessInstanceKey(), record.getProcessDefinitionKey(), event.getId(), repeatingInterval, writer, sideEffects::accept);
}
use of io.camunda.zeebe.engine.processing.common.Failure in project zeebe by zeebe-io.
the class CatchEventAnalyzer method findCatchEvent.
public Either<Failure, CatchEventTuple> findCatchEvent(final DirectBuffer errorCode, ElementInstance instance, final Optional<DirectBuffer> jobErrorMessage) {
// assuming that error events are used rarely
// - just walk through the scope hierarchy and look for a matching catch event
final ArrayList<DirectBuffer> availableCatchEvents = new ArrayList<>();
while (instance != null && instance.isActive()) {
final var instanceRecord = instance.getValue();
final var process = getProcess(instanceRecord.getProcessDefinitionKey());
final var found = findCatchEventInProcess(errorCode, process, instance);
if (found.isRight()) {
return Either.right(found.get());
} else {
availableCatchEvents.addAll(found.getLeft());
}
// find in parent process instance if exists
final var parentElementInstanceKey = instanceRecord.getParentElementInstanceKey();
instance = elementInstanceState.getInstance(parentElementInstanceKey);
}
final String incidentErrorMessage = String.format("Expected to throw an error event with the code '%s'%s, but it was not caught.%s", BufferUtil.bufferAsString(errorCode), jobErrorMessage.isPresent() && jobErrorMessage.get().capacity() > 0 ? String.format(" with message '%s'", BufferUtil.bufferAsString(jobErrorMessage.get())) : "", availableCatchEvents.isEmpty() ? " No error events are available in the scope." : String.format(" Available error events are [%s]", availableCatchEvents.stream().map(BufferUtil::bufferAsString).collect(Collectors.joining(", "))));
// no matching catch event found
return Either.left(new Failure(incidentErrorMessage, ErrorType.UNHANDLED_ERROR_EVENT));
}
Aggregations