Search in sources :

Example 1 with StateMachineConfig

use of io.seata.saga.engine.StateMachineConfig in project seata by seata.

the class ServiceTaskHandlerInterceptor method preProcess.

@Override
public void preProcess(ProcessContext context) throws EngineExecutionException {
    StateInstruction instruction = context.getInstruction(StateInstruction.class);
    StateMachineInstance stateMachineInstance = (StateMachineInstance) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_INST);
    StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
    if (EngineUtils.isTimeout(stateMachineInstance.getGmtUpdated(), stateMachineConfig.getTransOperationTimeout())) {
        String message = "Saga Transaction [stateMachineInstanceId:" + stateMachineInstance.getId() + "] has timed out, stop execution now.";
        LOGGER.error(message);
        EngineExecutionException exception = ExceptionUtils.createEngineExecutionException(null, FrameworkErrorCode.StateMachineExecutionTimeout, message, stateMachineInstance, instruction.getStateName());
        EngineUtils.failStateMachine(context, exception);
        throw exception;
    }
    StateInstanceImpl stateInstance = new StateInstanceImpl();
    Map<String, Object> contextVariables = (Map<String, Object>) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT);
    ServiceTaskStateImpl state = (ServiceTaskStateImpl) instruction.getState(context);
    List<Object> serviceInputParams = null;
    if (contextVariables != null) {
        try {
            serviceInputParams = ParameterUtils.createInputParams(stateMachineConfig.getExpressionFactoryManager(), stateInstance, state, contextVariables);
        } catch (Exception e) {
            String message = "Task [" + state.getName() + "] input parameters assign failed, please check 'Input' expression:" + e.getMessage();
            EngineExecutionException exception = ExceptionUtils.createEngineExecutionException(e, FrameworkErrorCode.VariablesAssignError, message, stateMachineInstance, state.getName());
            EngineUtils.failStateMachine(context, exception);
            throw exception;
        }
    }
    ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_INPUT_PARAMS, serviceInputParams);
    stateInstance.setMachineInstanceId(stateMachineInstance.getId());
    stateInstance.setStateMachineInstance(stateMachineInstance);
    Object isForCompensation = state.isForCompensation();
    if (context.hasVariable(DomainConstants.VAR_NAME_IS_LOOP_STATE) && !Boolean.TRUE.equals(isForCompensation)) {
        stateInstance.setName(LoopTaskUtils.generateLoopStateName(context, state.getName()));
        StateInstance lastRetriedStateInstance = LoopTaskUtils.findOutLastRetriedStateInstance(stateMachineInstance, stateInstance.getName());
        stateInstance.setStateIdRetriedFor(lastRetriedStateInstance == null ? null : lastRetriedStateInstance.getId());
    } else {
        stateInstance.setName(state.getName());
        stateInstance.setStateIdRetriedFor((String) context.getVariable(state.getName() + DomainConstants.VAR_NAME_RETRIED_STATE_INST_ID));
    }
    stateInstance.setGmtStarted(new Date());
    stateInstance.setGmtUpdated(stateInstance.getGmtStarted());
    stateInstance.setStatus(ExecutionStatus.RU);
    if (StringUtils.hasLength(stateInstance.getBusinessKey())) {
        ((Map<String, Object>) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT)).put(state.getName() + DomainConstants.VAR_NAME_BUSINESSKEY, stateInstance.getBusinessKey());
    }
    stateInstance.setType(state.getType());
    stateInstance.setForUpdate(state.isForUpdate());
    stateInstance.setServiceName(state.getServiceName());
    stateInstance.setServiceMethod(state.getServiceMethod());
    stateInstance.setServiceType(state.getServiceType());
    if (isForCompensation != null && (Boolean) isForCompensation) {
        CompensationHolder compensationHolder = CompensationHolder.getCurrent(context, true);
        StateInstance stateToBeCompensated = compensationHolder.getStatesNeedCompensation().get(state.getName());
        if (stateToBeCompensated != null) {
            stateToBeCompensated.setCompensationState(stateInstance);
            stateInstance.setStateIdCompensatedFor(stateToBeCompensated.getId());
        } else {
            LOGGER.error("Compensation State[{}] has no state to compensate, maybe this is a bug.", state.getName());
        }
        CompensationHolder.getCurrent(context, true).addForCompensationState(stateInstance.getName(), stateInstance);
    }
    if (DomainConstants.OPERATION_NAME_FORWARD.equals(context.getVariable(DomainConstants.VAR_NAME_OPERATION_NAME)) && StringUtils.isEmpty(stateInstance.getStateIdRetriedFor()) && !state.isForCompensation()) {
        List<StateInstance> stateList = stateMachineInstance.getStateList();
        if (CollectionUtils.isNotEmpty(stateList)) {
            for (int i = stateList.size() - 1; i >= 0; i--) {
                StateInstance executedState = stateList.get(i);
                if (stateInstance.getName().equals(executedState.getName())) {
                    stateInstance.setStateIdRetriedFor(executedState.getId());
                    executedState.setIgnoreStatus(true);
                    break;
                }
            }
        }
    }
    stateInstance.setInputParams(serviceInputParams);
    if (stateMachineInstance.getStateMachine().isPersist() && state.isPersist() && stateMachineConfig.getStateLogStore() != null) {
        try {
            stateMachineConfig.getStateLogStore().recordStateStarted(stateInstance, context);
        } catch (Exception e) {
            String message = "Record state[" + state.getName() + "] started failed, stateMachineInstance[" + stateMachineInstance.getId() + "], Reason: " + e.getMessage();
            EngineExecutionException exception = ExceptionUtils.createEngineExecutionException(e, FrameworkErrorCode.ExceptionCaught, message, stateMachineInstance, state.getName());
            EngineUtils.failStateMachine(context, exception);
            throw exception;
        }
    }
    if (StringUtils.isEmpty(stateInstance.getId())) {
        stateInstance.setId(stateMachineConfig.getSeqGenerator().generate(DomainConstants.SEQ_ENTITY_STATE_INST));
    }
    stateMachineInstance.putStateInstance(stateInstance.getId(), stateInstance);
    ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_STATE_INST, stateInstance);
}
Also used : CompensationHolder(io.seata.saga.engine.pcext.utils.CompensationHolder) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) StateInstanceImpl(io.seata.saga.statelang.domain.impl.StateInstanceImpl) HierarchicalProcessContext(io.seata.saga.proctrl.HierarchicalProcessContext) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) Date(java.util.Date) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance) ServiceTaskStateImpl(io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl) StateMachineConfig(io.seata.saga.engine.StateMachineConfig) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 2 with StateMachineConfig

use of io.seata.saga.engine.StateMachineConfig in project seata by seata.

the class LoopTaskUtils method putContextToParent.

/**
 * put loop out params to parent context
 *
 * @param context
 */
public static void putContextToParent(ProcessContext context, List<ProcessContext> subContextList, State state) {
    Map<String, Object> contextVariables = (Map<String, Object>) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT);
    if (CollectionUtils.isNotEmpty(subContextList)) {
        StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
        List<Map<String, Object>> subContextVariables = new ArrayList<>();
        for (ProcessContext subProcessContext : subContextList) {
            StateInstance stateInstance = (StateInstance) subProcessContext.getVariable(DomainConstants.VAR_NAME_STATE_INST);
            Map<String, Object> outputVariablesToContext = ParameterUtils.createOutputParams(stateMachineConfig.getExpressionFactoryManager(), (AbstractTaskState) state, stateInstance.getOutputParams());
            subContextVariables.add(outputVariablesToContext);
        }
        contextVariables.put(DomainConstants.LOOP_RESULT, subContextVariables);
    }
}
Also used : ArrayList(java.util.ArrayList) StateMachineConfig(io.seata.saga.engine.StateMachineConfig) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ProcessContext(io.seata.saga.proctrl.ProcessContext) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 3 with StateMachineConfig

use of io.seata.saga.engine.StateMachineConfig in project seata by seata.

the class LoopTaskUtils method isForSubStateMachineForward.

/**
 * forward with subStateMachine should check each loop state's status
 *
 * @param context
 * @return
 */
public static boolean isForSubStateMachineForward(ProcessContext context) {
    StateMachineInstance stateMachineInstance = (StateMachineInstance) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_INST);
    StateInstruction instruction = context.getInstruction(StateInstruction.class);
    StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
    StateInstance lastRetriedStateInstance = LoopTaskUtils.findOutLastRetriedStateInstance(stateMachineInstance, LoopTaskUtils.generateLoopStateName(context, instruction.getStateName()));
    if (null != lastRetriedStateInstance && DomainConstants.STATE_TYPE_SUB_STATE_MACHINE.equals(lastRetriedStateInstance.getType()) && !ExecutionStatus.SU.equals(lastRetriedStateInstance.getCompensationStatus())) {
        while (StringUtils.isNotBlank(lastRetriedStateInstance.getStateIdRetriedFor())) {
            lastRetriedStateInstance = stateMachineConfig.getStateLogStore().getStateInstance(lastRetriedStateInstance.getStateIdRetriedFor(), lastRetriedStateInstance.getMachineInstanceId());
        }
        List<StateMachineInstance> subInst = stateMachineConfig.getStateLogStore().queryStateMachineInstanceByParentId(EngineUtils.generateParentId(lastRetriedStateInstance));
        if (CollectionUtils.isNotEmpty(subInst)) {
            if (ExecutionStatus.SU.equals(subInst.get(0).getCompensationStatus())) {
                return false;
            }
        }
        if (ExecutionStatus.UN.equals(subInst.get(0).getCompensationStatus())) {
            throw new ForwardInvalidException("Last forward execution state instance is SubStateMachine and compensation status is " + "[UN], Operation[forward] denied, stateInstanceId:" + lastRetriedStateInstance.getId(), FrameworkErrorCode.OperationDenied);
        }
        return true;
    }
    return false;
}
Also used : StateInstruction(io.seata.saga.engine.pcext.StateInstruction) ForwardInvalidException(io.seata.saga.engine.exception.ForwardInvalidException) StateMachineConfig(io.seata.saga.engine.StateMachineConfig) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 4 with StateMachineConfig

use of io.seata.saga.engine.StateMachineConfig in project seata by seata.

the class LoopTaskUtils method getEvaluator.

/**
 * get loop completion condition evaluator
 *
 * @param context
 * @param completionCondition
 * @return
 */
private static ExpressionEvaluator getEvaluator(ProcessContext context, String completionCondition) {
    if (StringUtils.isBlank(completionCondition)) {
        completionCondition = DEFAULT_COMPLETION_CONDITION;
    }
    if (!EXPRESSION_EVALUATOR_MAP.containsKey(completionCondition)) {
        StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
        ExpressionEvaluator expressionEvaluator = (ExpressionEvaluator) stateMachineConfig.getEvaluatorFactoryManager().getEvaluatorFactory(EvaluatorFactoryManager.EVALUATOR_TYPE_DEFAULT).createEvaluator(completionCondition);
        expressionEvaluator.setRootObjectName(null);
        EXPRESSION_EVALUATOR_MAP.put(completionCondition, expressionEvaluator);
    }
    return EXPRESSION_EVALUATOR_MAP.get(completionCondition);
}
Also used : StateMachineConfig(io.seata.saga.engine.StateMachineConfig) ExpressionEvaluator(io.seata.saga.engine.evaluation.expression.ExpressionEvaluator)

Example 5 with StateMachineConfig

use of io.seata.saga.engine.StateMachineConfig in project seata by seata.

the class LoopTaskUtils method getLoopConfig.

/**
 * get Loop Config from State
 *
 * @param context
 * @param currentState
 * @return currentState loop config if satisfied, else {@literal null}
 */
public static Loop getLoopConfig(ProcessContext context, State currentState) {
    if (matchLoop(currentState)) {
        AbstractTaskState taskState = (AbstractTaskState) currentState;
        StateMachineInstance stateMachineInstance = (StateMachineInstance) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_INST);
        StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
        if (null != taskState.getLoop()) {
            Loop loop = taskState.getLoop();
            String collectionName = loop.getCollection();
            if (StringUtils.isNotBlank(collectionName)) {
                Object expression = ParameterUtils.createValueExpression(stateMachineConfig.getExpressionFactoryManager(), collectionName);
                Object collection = ParameterUtils.getValue(expression, stateMachineInstance.getContext(), null);
                if (collection instanceof Collection && ((Collection) collection).size() > 0) {
                    LoopContextHolder.getCurrent(context, true).setCollection((Collection) collection);
                    return loop;
                }
            }
            LOGGER.warn("State [{}] loop collection param [{}] invalid", currentState.getName(), collectionName);
        }
    }
    return null;
}
Also used : AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) Loop(io.seata.saga.statelang.domain.TaskState.Loop) Collection(java.util.Collection) StateMachineConfig(io.seata.saga.engine.StateMachineConfig) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance)

Aggregations

StateMachineConfig (io.seata.saga.engine.StateMachineConfig)24 StateMachineInstance (io.seata.saga.statelang.domain.StateMachineInstance)15 EngineExecutionException (io.seata.saga.engine.exception.EngineExecutionException)14 StateInstruction (io.seata.saga.engine.pcext.StateInstruction)11 StateInstance (io.seata.saga.statelang.domain.StateInstance)8 Map (java.util.Map)8 DbStateMachineConfig (io.seata.saga.engine.config.DbStateMachineConfig)5 DefaultStateMachineConfig (io.seata.saga.engine.impl.DefaultStateMachineConfig)5 HierarchicalProcessContext (io.seata.saga.proctrl.HierarchicalProcessContext)5 ArrayList (java.util.ArrayList)5 Date (java.util.Date)5 ScriptTaskStateImpl (io.seata.saga.statelang.domain.impl.ScriptTaskStateImpl)3 ServiceTaskStateImpl (io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl)3 GlobalTransaction (io.seata.tm.api.GlobalTransaction)3 ExecutionException (io.seata.tm.api.TransactionalExecutor.ExecutionException)3 LinkedHashMap (java.util.LinkedHashMap)3 List (java.util.List)3 TransactionException (io.seata.core.exception.TransactionException)2 AsyncCallback (io.seata.saga.engine.AsyncCallback)2 ExpressionEvaluator (io.seata.saga.engine.evaluation.expression.ExpressionEvaluator)2