Search in sources :

Example 26 with EngineExecutionException

use of io.seata.saga.engine.exception.EngineExecutionException in project seata by seata.

the class SpringBeanServiceInvoker method doInvoke.

protected Object doInvoke(ServiceTaskStateImpl state, Object[] input) throws Throwable {
    Object bean = applicationContext.getBean(state.getServiceName());
    Method method = state.getMethod();
    if (method == null) {
        synchronized (state) {
            method = state.getMethod();
            if (method == null) {
                method = findMethod(bean.getClass(), state.getServiceMethod(), state.getParameterTypes());
                if (method != null) {
                    state.setMethod(method);
                }
            }
        }
    }
    if (method == null) {
        throw new EngineExecutionException("No such method[" + state.getServiceMethod() + "] on BeanClass[" + bean.getClass() + "]", FrameworkErrorCode.NoSuchMethod);
    }
    Object[] args = new Object[method.getParameterCount()];
    try {
        Class[] paramTypes = method.getParameterTypes();
        if (input != null && input.length > 0) {
            int len = input.length < paramTypes.length ? input.length : paramTypes.length;
            for (int i = 0; i < len; i++) {
                args[i] = toJavaObject(input[i], paramTypes[i]);
            }
        }
    } catch (Exception e) {
        throw new EngineExecutionException(e, "Input to java object error, Method[" + state.getServiceMethod() + "] on BeanClass[" + bean.getClass() + "]", FrameworkErrorCode.InvalidParameter);
    }
    if (!Modifier.isPublic(method.getModifiers())) {
        throw new EngineExecutionException("Method[" + method.getName() + "] must be public", FrameworkErrorCode.MethodNotPublic);
    }
    Map<Retry, AtomicInteger> retryCountMap = new HashMap<>();
    while (true) {
        try {
            return invokeMethod(bean, method, args);
        } catch (Throwable e) {
            Retry matchedRetryConfig = matchRetryConfig(state.getRetry(), e);
            if (matchedRetryConfig == null) {
                throw e;
            }
            AtomicInteger retryCount = CollectionUtils.computeIfAbsent(retryCountMap, matchedRetryConfig, key -> new AtomicInteger(0));
            if (retryCount.intValue() >= matchedRetryConfig.getMaxAttempts()) {
                throw e;
            }
            double intervalSeconds = matchedRetryConfig.getIntervalSeconds();
            double backoffRate = matchedRetryConfig.getBackoffRate();
            long currentInterval = (long) (retryCount.intValue() > 0 ? (intervalSeconds * backoffRate * retryCount.intValue() * 1000) : (intervalSeconds * 1000));
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("Invoke Service[" + state.getServiceName() + "." + state.getServiceMethod() + "] failed, will retry after " + currentInterval + " millis, current retry count: " + retryCount.intValue(), e);
            }
            try {
                Thread.sleep(currentInterval);
            } catch (InterruptedException e1) {
                LOGGER.warn("Retry interval sleep error", e1);
            }
            retryCount.incrementAndGet();
        }
    }
}
Also used : ExceptionUtils(io.seata.saga.engine.utils.ExceptionUtils) ThreadPoolExecutor(java.util.concurrent.ThreadPoolExecutor) LoggerFactory(org.slf4j.LoggerFactory) HashMap(java.util.HashMap) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) ArrayList(java.util.ArrayList) BigDecimal(java.math.BigDecimal) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Map(java.util.Map) JsonParser(io.seata.saga.statelang.parser.JsonParser) BigInteger(java.math.BigInteger) ServiceInvoker(io.seata.saga.engine.invoker.ServiceInvoker) Method(java.lang.reflect.Method) ServiceTaskStateHandler(io.seata.saga.engine.pcext.handlers.ServiceTaskStateHandler) JsonParserFactory(io.seata.saga.statelang.parser.JsonParserFactory) Logger(org.slf4j.Logger) Retry(io.seata.saga.statelang.domain.TaskState.Retry) CollectionUtils(io.seata.common.util.CollectionUtils) ServiceTaskState(io.seata.saga.statelang.domain.ServiceTaskState) FrameworkErrorCode(io.seata.common.exception.FrameworkErrorCode) ApplicationContext(org.springframework.context.ApplicationContext) InvocationTargetException(java.lang.reflect.InvocationTargetException) List(java.util.List) ServiceTaskStateImpl(io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl) Modifier(java.lang.reflect.Modifier) ApplicationContextAware(org.springframework.context.ApplicationContextAware) BeanUtils(org.springframework.beans.BeanUtils) HashMap(java.util.HashMap) Method(java.lang.reflect.Method) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) InvocationTargetException(java.lang.reflect.InvocationTargetException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Retry(io.seata.saga.statelang.domain.TaskState.Retry)

Example 27 with EngineExecutionException

use of io.seata.saga.engine.exception.EngineExecutionException in project seata by seata.

the class ProcessCtrlStateMachineEngine method checkStatus.

/**
 * Check if the status is legal
 *
 * @param stateMachineInstance
 * @param acceptStatus
 * @param denyStatus
 * @param status
 * @param compenStatus
 * @param operation
 * @return
 */
protected boolean checkStatus(StateMachineInstance stateMachineInstance, ExecutionStatus[] acceptStatus, ExecutionStatus[] denyStatus, ExecutionStatus status, ExecutionStatus compenStatus, String operation) {
    if (status != null && compenStatus != null) {
        throw new EngineExecutionException("status and compensationStatus are not supported at the same time", FrameworkErrorCode.InvalidParameter);
    }
    if (status == null && compenStatus == null) {
        throw new EngineExecutionException("status and compensationStatus must input at least one", FrameworkErrorCode.InvalidParameter);
    }
    if (ExecutionStatus.SU.equals(compenStatus)) {
        String message = buildExceptionMessage(stateMachineInstance, null, null, null, ExecutionStatus.SU, operation);
        throw new EngineExecutionException(message, FrameworkErrorCode.OperationDenied);
    }
    if (stateMachineInstance.isRunning() && !EngineUtils.isTimeout(stateMachineInstance.getGmtUpdated(), stateMachineConfig.getTransOperationTimeout())) {
        throw new EngineExecutionException("StateMachineInstance [id:" + stateMachineInstance.getId() + "] is running, operation[" + operation + "] denied", FrameworkErrorCode.OperationDenied);
    }
    if ((denyStatus == null || denyStatus.length == 0) && (acceptStatus == null || acceptStatus.length == 0)) {
        throw new EngineExecutionException("StateMachineInstance[id:" + stateMachineInstance.getId() + "], acceptable status and deny status must input at least one", FrameworkErrorCode.InvalidParameter);
    }
    ExecutionStatus currentStatus = (status != null) ? status : compenStatus;
    if (!(denyStatus == null || denyStatus.length == 0)) {
        for (ExecutionStatus tempDenyStatus : denyStatus) {
            if (tempDenyStatus.compareTo(currentStatus) == 0) {
                String message = buildExceptionMessage(stateMachineInstance, acceptStatus, denyStatus, status, compenStatus, operation);
                throw new EngineExecutionException(message, FrameworkErrorCode.OperationDenied);
            }
        }
    }
    if (acceptStatus == null || acceptStatus.length == 0) {
        return true;
    } else {
        for (ExecutionStatus tempStatus : acceptStatus) {
            if (tempStatus.compareTo(currentStatus) == 0) {
                return true;
            }
        }
    }
    String message = buildExceptionMessage(stateMachineInstance, acceptStatus, denyStatus, status, compenStatus, operation);
    throw new EngineExecutionException(message, FrameworkErrorCode.OperationDenied);
}
Also used : ExecutionStatus(io.seata.saga.statelang.domain.ExecutionStatus) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException)

Example 28 with EngineExecutionException

use of io.seata.saga.engine.exception.EngineExecutionException in project seata by seata.

the class ProcessCtrlStateMachineEngine method forwardInternal.

protected StateMachineInstance forwardInternal(String stateMachineInstId, Map<String, Object> replaceParams, boolean skip, boolean async, AsyncCallback callback) throws EngineExecutionException {
    StateMachineInstance stateMachineInstance = reloadStateMachineInstance(stateMachineInstId);
    if (stateMachineInstance == null) {
        throw new ForwardInvalidException("StateMachineInstance is not exits", FrameworkErrorCode.StateMachineInstanceNotExists);
    }
    if (ExecutionStatus.SU.equals(stateMachineInstance.getStatus()) && stateMachineInstance.getCompensationStatus() == null) {
        return stateMachineInstance;
    }
    ExecutionStatus[] acceptStatus = new ExecutionStatus[] { ExecutionStatus.FA, ExecutionStatus.UN, ExecutionStatus.RU };
    checkStatus(stateMachineInstance, acceptStatus, null, stateMachineInstance.getStatus(), null, "forward");
    List<StateInstance> actList = stateMachineInstance.getStateList();
    if (CollectionUtils.isEmpty(actList)) {
        throw new ForwardInvalidException("StateMachineInstance[id:" + stateMachineInstId + "] has no stateInstance, pls start a new StateMachine execution instead", FrameworkErrorCode.OperationDenied);
    }
    StateInstance lastForwardState = findOutLastForwardStateInstance(actList);
    if (lastForwardState == null) {
        throw new ForwardInvalidException("StateMachineInstance[id:" + stateMachineInstId + "] Cannot find last forward execution stateInstance", FrameworkErrorCode.OperationDenied);
    }
    ProcessContextBuilder contextBuilder = ProcessContextBuilder.create().withProcessType(ProcessType.STATE_LANG).withOperationName(DomainConstants.OPERATION_NAME_FORWARD).withAsyncCallback(callback).withStateMachineInstance(stateMachineInstance).withStateInstance(lastForwardState).withStateMachineConfig(getStateMachineConfig()).withStateMachineEngine(this);
    contextBuilder.withIsAsyncExecution(async);
    ProcessContext context = contextBuilder.build();
    Map<String, Object> contextVariables = getStateMachineContextVariables(stateMachineInstance);
    if (replaceParams != null) {
        contextVariables.putAll(replaceParams);
    }
    putBusinesskeyToContextariables(stateMachineInstance, contextVariables);
    ConcurrentHashMap<String, Object> concurrentContextVariables = new ConcurrentHashMap<>(contextVariables.size());
    nullSafeCopy(contextVariables, concurrentContextVariables);
    context.setVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT, concurrentContextVariables);
    stateMachineInstance.setContext(concurrentContextVariables);
    String originStateName = EngineUtils.getOriginStateName(lastForwardState);
    State lastState = stateMachineInstance.getStateMachine().getState(originStateName);
    Loop loop = LoopTaskUtils.getLoopConfig(context, lastState);
    if (null != loop && ExecutionStatus.SU.equals(lastForwardState.getStatus())) {
        lastForwardState = LoopTaskUtils.findOutLastNeedForwardStateInstance(context);
    }
    context.setVariable(lastForwardState.getName() + DomainConstants.VAR_NAME_RETRIED_STATE_INST_ID, lastForwardState.getId());
    if (DomainConstants.STATE_TYPE_SUB_STATE_MACHINE.equals(lastForwardState.getType()) && !ExecutionStatus.SU.equals(lastForwardState.getCompensationStatus())) {
        context.setVariable(DomainConstants.VAR_NAME_IS_FOR_SUB_STATMACHINE_FORWARD, true);
    }
    if (!ExecutionStatus.SU.equals(lastForwardState.getStatus())) {
        lastForwardState.setIgnoreStatus(true);
    }
    try {
        StateInstruction inst = new StateInstruction();
        inst.setTenantId(stateMachineInstance.getTenantId());
        inst.setStateMachineName(stateMachineInstance.getStateMachine().getName());
        if (skip || ExecutionStatus.SU.equals(lastForwardState.getStatus())) {
            String next = null;
            State state = stateMachineInstance.getStateMachine().getState(EngineUtils.getOriginStateName(lastForwardState));
            if (state != null && state instanceof AbstractTaskState) {
                next = ((AbstractTaskState) state).getNext();
            }
            if (StringUtils.isEmpty(next)) {
                LOGGER.warn("Last Forward execution StateInstance was succeed, and it has not Next State , skip forward " + "operation");
                return stateMachineInstance;
            }
            inst.setStateName(next);
        } else {
            if (ExecutionStatus.RU.equals(lastForwardState.getStatus()) && !EngineUtils.isTimeout(lastForwardState.getGmtStarted(), stateMachineConfig.getServiceInvokeTimeout())) {
                throw new EngineExecutionException("State [" + lastForwardState.getName() + "] is running, operation[forward] denied", FrameworkErrorCode.OperationDenied);
            }
            inst.setStateName(EngineUtils.getOriginStateName(lastForwardState));
        }
        context.setInstruction(inst);
        stateMachineInstance.setStatus(ExecutionStatus.RU);
        stateMachineInstance.setRunning(true);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Operation [forward] started  stateMachineInstance[id:" + stateMachineInstance.getId() + "]");
        }
        if (stateMachineInstance.getStateMachine().isPersist()) {
            stateMachineConfig.getStateLogStore().recordStateMachineRestarted(stateMachineInstance, context);
        }
        loop = LoopTaskUtils.getLoopConfig(context, inst.getState(context));
        if (null != loop) {
            inst.setTemporaryState(new LoopStartStateImpl());
        }
        if (async) {
            stateMachineConfig.getAsyncProcessCtrlEventPublisher().publish(context);
        } else {
            stateMachineConfig.getProcessCtrlEventPublisher().publish(context);
        }
    } catch (EngineExecutionException e) {
        LOGGER.error("Operation [forward] failed", e);
        throw e;
    }
    return stateMachineInstance;
}
Also used : Loop(io.seata.saga.statelang.domain.TaskState.Loop) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) ForwardInvalidException(io.seata.saga.engine.exception.ForwardInvalidException) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) ProcessContext(io.seata.saga.proctrl.ProcessContext) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance) AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) LoopStartStateImpl(io.seata.saga.statelang.domain.impl.LoopStartStateImpl) ProcessContextBuilder(io.seata.saga.engine.utils.ProcessContextBuilder) ExecutionStatus(io.seata.saga.statelang.domain.ExecutionStatus) State(io.seata.saga.statelang.domain.State) AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 29 with EngineExecutionException

use of io.seata.saga.engine.exception.EngineExecutionException in project seata by seata.

the class ProcessCtrlStateMachineEngine method startInternal.

private StateMachineInstance startInternal(String stateMachineName, String tenantId, String businessKey, Map<String, Object> startParams, boolean async, AsyncCallback callback) throws EngineExecutionException {
    if (async && !stateMachineConfig.isEnableAsync()) {
        throw new EngineExecutionException("Asynchronous start is disabled. please set StateMachineConfig.enableAsync=true first.", FrameworkErrorCode.AsynchronousStartDisabled);
    }
    if (StringUtils.isEmpty(tenantId)) {
        tenantId = stateMachineConfig.getDefaultTenantId();
    }
    StateMachineInstance instance = createMachineInstance(stateMachineName, tenantId, businessKey, startParams);
    ProcessContextBuilder contextBuilder = ProcessContextBuilder.create().withProcessType(ProcessType.STATE_LANG).withOperationName(DomainConstants.OPERATION_NAME_START).withAsyncCallback(callback).withInstruction(new StateInstruction(stateMachineName, tenantId)).withStateMachineInstance(instance).withStateMachineConfig(getStateMachineConfig()).withStateMachineEngine(this);
    Map<String, Object> contextVariables;
    if (startParams != null) {
        contextVariables = new ConcurrentHashMap<>(startParams.size());
        nullSafeCopy(startParams, contextVariables);
    } else {
        contextVariables = new ConcurrentHashMap<>();
    }
    instance.setContext(contextVariables);
    contextBuilder.withStateMachineContextVariables(contextVariables);
    contextBuilder.withIsAsyncExecution(async);
    ProcessContext processContext = contextBuilder.build();
    if (instance.getStateMachine().isPersist() && stateMachineConfig.getStateLogStore() != null) {
        stateMachineConfig.getStateLogStore().recordStateMachineStarted(instance, processContext);
    }
    if (StringUtils.isEmpty(instance.getId())) {
        instance.setId(stateMachineConfig.getSeqGenerator().generate(DomainConstants.SEQ_ENTITY_STATE_MACHINE_INST));
    }
    StateInstruction stateInstruction = processContext.getInstruction(StateInstruction.class);
    Loop loop = LoopTaskUtils.getLoopConfig(processContext, stateInstruction.getState(processContext));
    if (null != loop) {
        stateInstruction.setTemporaryState(new LoopStartStateImpl());
    }
    if (async) {
        stateMachineConfig.getAsyncProcessCtrlEventPublisher().publish(processContext);
    } else {
        stateMachineConfig.getProcessCtrlEventPublisher().publish(processContext);
    }
    return instance;
}
Also used : Loop(io.seata.saga.statelang.domain.TaskState.Loop) LoopStartStateImpl(io.seata.saga.statelang.domain.impl.LoopStartStateImpl) ProcessContextBuilder(io.seata.saga.engine.utils.ProcessContextBuilder) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) ProcessContext(io.seata.saga.proctrl.ProcessContext) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance)

Example 30 with EngineExecutionException

use of io.seata.saga.engine.exception.EngineExecutionException in project seata by seata.

the class ProcessCtrlStateMachineEngine method replayContextVariables.

protected Map<String, Object> replayContextVariables(StateMachineInstance stateMachineInstance) {
    Map<String, Object> contextVariables = new HashMap<>();
    if (stateMachineInstance.getStartParams() == null) {
        contextVariables.putAll(stateMachineInstance.getStartParams());
    }
    List<StateInstance> stateInstanceList = stateMachineInstance.getStateList();
    if (CollectionUtils.isEmpty(stateInstanceList)) {
        return contextVariables;
    }
    for (StateInstance stateInstance : stateInstanceList) {
        Object serviceOutputParams = stateInstance.getOutputParams();
        if (serviceOutputParams != null) {
            ServiceTaskStateImpl state = (ServiceTaskStateImpl) stateMachineInstance.getStateMachine().getState(EngineUtils.getOriginStateName(stateInstance));
            if (state == null) {
                throw new EngineExecutionException("Cannot find State by state name [" + stateInstance.getName() + "], may be this is a bug", FrameworkErrorCode.ObjectNotExists);
            }
            if (CollectionUtils.isNotEmpty(state.getOutput())) {
                try {
                    Map<String, Object> outputVariablesToContext = ParameterUtils.createOutputParams(stateMachineConfig.getExpressionFactoryManager(), state, serviceOutputParams);
                    if (CollectionUtils.isNotEmpty(outputVariablesToContext)) {
                        contextVariables.putAll(outputVariablesToContext);
                    }
                    if (StringUtils.hasLength(stateInstance.getBusinessKey())) {
                        contextVariables.put(state.getName() + DomainConstants.VAR_NAME_BUSINESSKEY, stateInstance.getBusinessKey());
                    }
                } catch (Exception e) {
                    throw new EngineExecutionException(e, "Context variables replay faied", FrameworkErrorCode.ContextVariableReplayFailed);
                }
            }
        }
    }
    return contextVariables;
}
Also used : HashMap(java.util.HashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) ForwardInvalidException(io.seata.saga.engine.exception.ForwardInvalidException) StateInstance(io.seata.saga.statelang.domain.StateInstance) ServiceTaskStateImpl(io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl)

Aggregations

EngineExecutionException (io.seata.saga.engine.exception.EngineExecutionException)34 StateMachineInstance (io.seata.saga.statelang.domain.StateMachineInstance)20 StateMachineConfig (io.seata.saga.engine.StateMachineConfig)14 StateInstruction (io.seata.saga.engine.pcext.StateInstruction)14 StateInstance (io.seata.saga.statelang.domain.StateInstance)11 Map (java.util.Map)10 GlobalTransaction (io.seata.tm.api.GlobalTransaction)6 ArrayList (java.util.ArrayList)6 HierarchicalProcessContext (io.seata.saga.proctrl.HierarchicalProcessContext)5 ServiceTaskStateImpl (io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl)5 Date (java.util.Date)5 List (java.util.List)5 TransactionException (io.seata.core.exception.TransactionException)4 ForwardInvalidException (io.seata.saga.engine.exception.ForwardInvalidException)4 ProcessContext (io.seata.saga.proctrl.ProcessContext)4 ExecutionStatus (io.seata.saga.statelang.domain.ExecutionStatus)4 State (io.seata.saga.statelang.domain.State)4 StateMachine (io.seata.saga.statelang.domain.StateMachine)4 ExecutionException (io.seata.tm.api.TransactionalExecutor.ExecutionException)4 HashMap (java.util.HashMap)4