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();
}
}
}
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);
}
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;
}
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;
}
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;
}
Aggregations