class AbstractAction implements Action<S, E> {

    @Override
    public void execute(StateContext<S, E> context) {
        FlowParameters flowParameters = (FlowParameters) context.getMessageHeader(MessageFactory.HEADERS.FLOW_PARAMETERS.name());
        ThreadBasedUserCrnProvider.doAs(flowParameters.getFlowTriggerUserCrn(), () -> {
            MDCBuilder.addFlowId(flowParameters.getFlowId());
            P payload = convertPayload(context.getMessageHeader(MessageFactory.HEADERS.DATA.name()));
            C flowContext = null;
            try {
                Map<Object, Object> variables = context.getExtendedState().getVariables();
                prepareExecution(payload, variables);
                String flowStateName = String.valueOf(variables.get(FLOW_STATE_NAME));
                Span activeSpan = tracer.activeSpan();
                String operationName = context.getEvent().name();
                SpanContext spanContext = flowParameters.getSpanContext();
                if (FlowTracingUtil.isActiveSpanReusable(activeSpan, spanContext, operationName)) {
                    LOGGER.debug("Reusing existing span. {}", activeSpan.context());
                    flowContext = createFlowContext(flowParameters, context, payload);
                    executeAction(context, payload, flowContext, variables, flowStateName);
                } else {
                    Span span = FlowTracingUtil.getSpan(tracer, operationName, spanContext, flowParameters.getFlowId(),
                            null, flowParameters.getFlowTriggerUserCrn());
                    spanContext = FlowTracingUtil.useOrCreateSpanContext(spanContext, span);
                    flowParameters.setSpanContext(spanContext);
                    try (Scope ignored = tracer.activateSpan(span)) {
                        flowContext = createFlowContext(flowParameters, context, payload);
                        executeAction(context, payload, flowContext, variables, flowStateName);
                    } finally {
                        span.finish();
                    }
                }
            } catch (Exception ex) {
                LOGGER.error("Error during execution of " + getClass().getName(), ex);
                if (failureEvent != null) {
                    sendEvent(flowParameters, failureEvent.event(), getFailurePayload(payload, Optional.ofNullable(flowContext), ex), Map.of());
                } else {
                    LOGGER.error("Missing error handling for " + getClass().getName());
                    throw new CloudbreakServiceException("Missing error handling for " + getClass().getName());
                }
            }
        });
    }

    protected  AbstractAction(Class<P> payloadClass);

}