Search in sources :

Example 1 with HierarchicalProcessContext

use of io.seata.saga.proctrl.HierarchicalProcessContext 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 HierarchicalProcessContext

use of io.seata.saga.proctrl.HierarchicalProcessContext in project seata by seata.

the class EngineUtils method handleException.

/**
 * Handle exceptions while ServiceTask or ScriptTask Executing
 *
 * @param context
 * @param state
 * @param e
 */
public static void handleException(ProcessContext context, AbstractTaskState state, Throwable e) {
    List<ExceptionMatch> catches = state.getCatches();
    if (CollectionUtils.isNotEmpty(catches)) {
        for (TaskState.ExceptionMatch exceptionMatch : catches) {
            List<String> exceptions = exceptionMatch.getExceptions();
            List<Class<? extends Exception>> exceptionClasses = exceptionMatch.getExceptionClasses();
            if (CollectionUtils.isNotEmpty(exceptions)) {
                if (exceptionClasses == null) {
                    synchronized (exceptionMatch) {
                        exceptionClasses = exceptionMatch.getExceptionClasses();
                        if (exceptionClasses == null) {
                            exceptionClasses = new ArrayList<>(exceptions.size());
                            for (String expStr : exceptions) {
                                Class<? extends Exception> expClass = null;
                                try {
                                    expClass = (Class<? extends Exception>) ScriptTaskStateHandler.class.getClassLoader().loadClass(expStr);
                                } catch (Exception e1) {
                                    LOGGER.warn("Cannot Load Exception Class by getClass().getClassLoader()", e1);
                                    try {
                                        expClass = (Class<? extends Exception>) Thread.currentThread().getContextClassLoader().loadClass(expStr);
                                    } catch (Exception e2) {
                                        LOGGER.warn("Cannot Load Exception Class by Thread.currentThread()" + ".getContextClassLoader()", e2);
                                    }
                                }
                                if (expClass != null) {
                                    exceptionClasses.add(expClass);
                                }
                            }
                            exceptionMatch.setExceptionClasses(exceptionClasses);
                        }
                    }
                }
                for (Class<? extends Exception> expClass : exceptionClasses) {
                    if (expClass.isAssignableFrom(e.getClass())) {
                        ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_CURRENT_EXCEPTION_ROUTE, exceptionMatch.getNext());
                        return;
                    }
                }
            }
        }
    }
    LOGGER.error("Task execution failed and no catches configured");
    ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_IS_EXCEPTION_NOT_CATCH, true);
}
Also used : ExceptionMatch(io.seata.saga.statelang.domain.TaskState.ExceptionMatch) HierarchicalProcessContext(io.seata.saga.proctrl.HierarchicalProcessContext) ScriptTaskStateHandler(io.seata.saga.engine.pcext.handlers.ScriptTaskStateHandler) TaskState(io.seata.saga.statelang.domain.TaskState) AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) ExceptionMatch(io.seata.saga.statelang.domain.TaskState.ExceptionMatch)

Example 3 with HierarchicalProcessContext

use of io.seata.saga.proctrl.HierarchicalProcessContext in project seata by seata.

the class ScriptTaskStateHandler method process.

@Override
public void process(ProcessContext context) throws EngineExecutionException {
    StateInstruction instruction = context.getInstruction(StateInstruction.class);
    ScriptTaskStateImpl state = (ScriptTaskStateImpl) instruction.getState(context);
    String scriptType = state.getScriptType();
    String scriptContent = state.getScriptContent();
    Object result;
    try {
        List<Object> input = (List<Object>) context.getVariable(DomainConstants.VAR_NAME_INPUT_PARAMS);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(">>>>>>>>>>>>>>>>>>>>>> Start to execute ScriptTaskState[{}], ScriptType[{}], Input:{}", state.getName(), scriptType, input);
        }
        StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
        ScriptEngine scriptEngine = getScriptEngineFromCache(scriptType, stateMachineConfig.getScriptEngineManager());
        if (scriptEngine == null) {
            throw new EngineExecutionException("No such ScriptType[" + scriptType + "]", FrameworkErrorCode.ObjectNotExists);
        }
        Bindings bindings = null;
        Map<String, Object> inputMap = null;
        if (CollectionUtils.isNotEmpty(input) && input.get(0) instanceof Map) {
            inputMap = (Map<String, Object>) input.get(0);
        }
        List<Object> inputExps = state.getInput();
        if (CollectionUtils.isNotEmpty(inputExps) && inputExps.get(0) instanceof Map) {
            Map<String, Object> inputExpMap = (Map<String, Object>) inputExps.get(0);
            if (inputExpMap.size() > 0) {
                bindings = new SimpleBindings();
                for (String property : inputExpMap.keySet()) {
                    if (inputMap != null && inputMap.containsKey(property)) {
                        bindings.put(property, inputMap.get(property));
                    } else {
                        // if we do not bind the null value property, groovy will throw MissingPropertyException
                        bindings.put(property, null);
                    }
                }
            }
        }
        if (bindings != null) {
            result = scriptEngine.eval(scriptContent, bindings);
        } else {
            result = scriptEngine.eval(scriptContent);
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("<<<<<<<<<<<<<<<<<<<<<< ScriptTaskState[{}], ScriptType[{}], Execute finish. result: {}", state.getName(), scriptType, result);
        }
        if (result != null) {
            ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_OUTPUT_PARAMS, result);
        }
    } catch (Throwable e) {
        LOGGER.error("<<<<<<<<<<<<<<<<<<<<<< ScriptTaskState[{}], ScriptTaskState[{}] Execute failed.", state.getName(), scriptType, e);
        ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_CURRENT_EXCEPTION, e);
        EngineUtils.handleException(context, state, e);
    }
}
Also used : StateInstruction(io.seata.saga.engine.pcext.StateInstruction) HierarchicalProcessContext(io.seata.saga.proctrl.HierarchicalProcessContext) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) Bindings(javax.script.Bindings) SimpleBindings(javax.script.SimpleBindings) ScriptEngine(javax.script.ScriptEngine) ScriptTaskStateImpl(io.seata.saga.statelang.domain.impl.ScriptTaskStateImpl) SimpleBindings(javax.script.SimpleBindings) ArrayList(java.util.ArrayList) List(java.util.List) StateMachineConfig(io.seata.saga.engine.StateMachineConfig) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Map(java.util.Map)

Example 4 with HierarchicalProcessContext

use of io.seata.saga.proctrl.HierarchicalProcessContext in project seata by seata.

the class LoopTaskHandlerInterceptor method preProcess.

@Override
public void preProcess(ProcessContext context) throws EngineExecutionException {
    if (context.hasVariable(DomainConstants.VAR_NAME_IS_LOOP_STATE)) {
        StateInstruction instruction = context.getInstruction(StateInstruction.class);
        AbstractTaskState currentState = (AbstractTaskState) instruction.getState(context);
        int loopCounter;
        Loop loop;
        // get loop config
        if (context.hasVariable(DomainConstants.VAR_NAME_CURRENT_COMPEN_TRIGGER_STATE)) {
            // compensate condition should get stateToBeCompensated 's config
            CompensationHolder compensationHolder = CompensationHolder.getCurrent(context, true);
            StateInstance stateToBeCompensated = compensationHolder.getStatesNeedCompensation().get(currentState.getName());
            AbstractTaskState compensateState = (AbstractTaskState) stateToBeCompensated.getStateMachineInstance().getStateMachine().getState(EngineUtils.getOriginStateName(stateToBeCompensated));
            loop = compensateState.getLoop();
            loopCounter = LoopTaskUtils.reloadLoopCounter(stateToBeCompensated.getName());
        } else {
            loop = currentState.getLoop();
            loopCounter = (int) context.getVariable(DomainConstants.LOOP_COUNTER);
        }
        Collection collection = LoopContextHolder.getCurrent(context, true).getCollection();
        Map<String, Object> contextVariables = (Map<String, Object>) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT);
        Map<String, Object> copyContextVariables = new ConcurrentHashMap<>(contextVariables);
        copyContextVariables.put(loop.getElementIndexName(), loopCounter);
        copyContextVariables.put(loop.getElementVariableName(), iterator(collection, loopCounter));
        ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT, copyContextVariables);
    }
}
Also used : Loop(io.seata.saga.statelang.domain.TaskState.Loop) CompensationHolder(io.seata.saga.engine.pcext.utils.CompensationHolder) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) HierarchicalProcessContext(io.seata.saga.proctrl.HierarchicalProcessContext) AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Map(java.util.Map) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 5 with HierarchicalProcessContext

use of io.seata.saga.proctrl.HierarchicalProcessContext in project seata by seata.

the class TaskStateRouter method compensateRoute.

private Instruction compensateRoute(ProcessContext context, State compensationTriggerState) {
    // and the compensation process is interrupted.
    if (Boolean.TRUE.equals(context.getVariable(DomainConstants.VAR_NAME_FIRST_COMPENSATION_STATE_STARTED))) {
        Exception exception = (Exception) context.getVariable(DomainConstants.VAR_NAME_CURRENT_EXCEPTION);
        if (exception != null) {
            EngineUtils.endStateMachine(context);
            return null;
        }
        StateInstance stateInstance = (StateInstance) context.getVariable(DomainConstants.VAR_NAME_STATE_INST);
        if (stateInstance != null && (!ExecutionStatus.SU.equals(stateInstance.getStatus()))) {
            EngineUtils.endStateMachine(context);
            return null;
        }
    }
    Stack<StateInstance> stateStackToBeCompensated = CompensationHolder.getCurrent(context, true).getStateStackNeedCompensation();
    if (!stateStackToBeCompensated.isEmpty()) {
        StateInstance stateToBeCompensated = stateStackToBeCompensated.pop();
        StateMachine stateMachine = (StateMachine) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE);
        State state = stateMachine.getState(EngineUtils.getOriginStateName(stateToBeCompensated));
        if (state != null && state instanceof AbstractTaskState) {
            AbstractTaskState taskState = (AbstractTaskState) state;
            StateInstruction instruction = context.getInstruction(StateInstruction.class);
            State compensateState = null;
            String compensateStateName = taskState.getCompensateState();
            if (StringUtils.hasLength(compensateStateName)) {
                compensateState = stateMachine.getState(compensateStateName);
            }
            if (compensateState == null && (taskState instanceof SubStateMachine)) {
                compensateState = ((SubStateMachine) taskState).getCompensateStateObject();
                instruction.setTemporaryState(compensateState);
            }
            if (compensateState == null) {
                EngineUtils.endStateMachine(context);
                return null;
            }
            instruction.setStateName(compensateState.getName());
            CompensationHolder.getCurrent(context, true).addToBeCompensatedState(compensateState.getName(), stateToBeCompensated);
            ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_FIRST_COMPENSATION_STATE_STARTED, true);
            if (compensateState instanceof CompensateSubStateMachineState) {
                ((HierarchicalProcessContext) context).setVariableLocally(compensateState.getName() + DomainConstants.VAR_NAME_SUB_MACHINE_PARENT_ID, EngineUtils.generateParentId(stateToBeCompensated));
            }
            return instruction;
        }
    }
    context.removeVariable(DomainConstants.VAR_NAME_CURRENT_COMPEN_TRIGGER_STATE);
    String compensationTriggerStateNext = compensationTriggerState.getNext();
    if (StringUtils.isEmpty(compensationTriggerStateNext)) {
        EngineUtils.endStateMachine(context);
        return null;
    }
    StateInstruction instruction = context.getInstruction(StateInstruction.class);
    instruction.setStateName(compensationTriggerStateNext);
    return instruction;
}
Also used : AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) StateMachine(io.seata.saga.statelang.domain.StateMachine) SubStateMachine(io.seata.saga.statelang.domain.SubStateMachine) CompensateSubStateMachineState(io.seata.saga.statelang.domain.CompensateSubStateMachineState) State(io.seata.saga.statelang.domain.State) AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) HierarchicalProcessContext(io.seata.saga.proctrl.HierarchicalProcessContext) CompensateSubStateMachineState(io.seata.saga.statelang.domain.CompensateSubStateMachineState) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) StateInstance(io.seata.saga.statelang.domain.StateInstance) SubStateMachine(io.seata.saga.statelang.domain.SubStateMachine)

Aggregations

HierarchicalProcessContext (io.seata.saga.proctrl.HierarchicalProcessContext)8 StateInstruction (io.seata.saga.engine.pcext.StateInstruction)7 EngineExecutionException (io.seata.saga.engine.exception.EngineExecutionException)6 StateMachineConfig (io.seata.saga.engine.StateMachineConfig)5 StateInstance (io.seata.saga.statelang.domain.StateInstance)4 Map (java.util.Map)4 StateMachineInstance (io.seata.saga.statelang.domain.StateMachineInstance)3 AbstractTaskState (io.seata.saga.statelang.domain.impl.AbstractTaskState)3 ArrayList (java.util.ArrayList)3 CompensationHolder (io.seata.saga.engine.pcext.utils.CompensationHolder)2 CompensateSubStateMachineState (io.seata.saga.statelang.domain.CompensateSubStateMachineState)2 Loop (io.seata.saga.statelang.domain.TaskState.Loop)2 ScriptTaskStateImpl (io.seata.saga.statelang.domain.impl.ScriptTaskStateImpl)2 ServiceTaskStateImpl (io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl)2 List (java.util.List)2 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)2 StateMachineEngine (io.seata.saga.engine.StateMachineEngine)1 ServiceInvoker (io.seata.saga.engine.invoker.ServiceInvoker)1 ScriptTaskStateHandler (io.seata.saga.engine.pcext.handlers.ScriptTaskStateHandler)1 LoopContextHolder (io.seata.saga.engine.pcext.utils.LoopContextHolder)1