Spring-statemachine fork一个region后不能进入join状态的问题

Spring-statemachine版本:当前最新的1.2.3.RELEASE版本

发现fork多个Region时,子状态全部完成后能够进入join状态。但是如果fork一个Region时Region完成后并不能进入join状态。状态机是StateMachineBuilder.builder()构建的。

然后debug源码很久,发现状态机构建的逻辑是这样的:
org.springframework.statemachine.config.AbstractStateMachineFactory

public StateMachine<S, E> getStateMachine(UUID uuid, String machineId) {
    ...
    Collection<StateData<S, E>> stateDatas = popSameParents(stateStack);
    // stateDatas代表一个区域(Region。拥有相同的父状态)的状态集合
    int initialCount = getInitialCount(stateDatas); //获得一个region的初始化状态的数目
    Collection<Collection<StateData<S, E>>> regionsStateDatas = splitIntoRegions(stateDatas);
    Collection<TransitionData<S, E>> transitionsData = getTransitionData(iterator.hasNext(), stateDatas, stateMachineModel);
    
    if (initialCount > 1) { // 判断初始化状态数大于1才会通过RegionState创建子状态机
        for (Collection<StateData<S, E>> regionStateDatas : regionsStateDatas) {
            machine = buildMachine(machineMap, stateMap, holderMap, regionStateDatas, transitionsData, resolveBeanFactory(stateMachineModel),
                contextEvents, defaultExtendedState, stateMachineModel.getTransitionsData(), resolveTaskExecutor(stateMachineModel),
                resolveTaskScheduler(stateMachineModel), machineId, null, stateMachineModel);
            regionStack.push(new MachineStackItem<S, E>(machine));
        }
    
        Collection<Region<S, E>> regions = new ArrayList<Region<S, E>>();
        while (!regionStack.isEmpty()) {
            MachineStackItem<S, E> pop = regionStack.pop();
            regions.add(pop.machine);
        }
        S parent = (S)peek.getParent();
        // 创建类型为RegionState的状态
        RegionState<S, E> rstate = buildRegionStateInternal(parent, regions, null, stateData != null ? stateData.getEntryActions() : null,
            stateData != null ? stateData.getExitActions() : null, new DefaultPseudoState<S, E>(PseudoStateKind.INITIAL));
        if (stateData != null) {
            stateMap.put(stateData.getState(), rstate);
        } else {
            // TODO: don't like that we create a last machine here
            Collection<State<S, E>> states = new ArrayList<State<S, E>>();
            states.add(rstate);
            Transition<S, E> initialTransition = new InitialTransition<S, E>(rstate);
            // 把RegionState转变成ObjectStateMachine子状态机
            StateMachine<S, E> m = buildStateMachineInternal(states, new ArrayList<Transition<S, E>>(), rstate, initialTransition,
                null, defaultExtendedState, null, contextEvents, resolveBeanFactory(stateMachineModel), resolveTaskExecutor(stateMachineModel),
                resolveTaskScheduler(stateMachineModel), beanName,
                machineId != null ? machineId : stateMachineModel.getConfigurationData().getMachineId(),
                uuid, stateMachineModel);
            machine = m;
        }
    } else { // 如果没有初始化状态或只有一个初始化状态,就构建一个普通的ObjectStateMachine子状态机
        machine = buildMachine(machineMap, stateMap, holderMap, stateDatas, transitionsData, resolveBeanFactory(stateMachineModel), contextEvents,
            defaultExtendedState, stateMachineModel.getTransitionsData(), resolveTaskExecutor(stateMachineModel), resolveTaskScheduler(stateMachineModel),
            machineId, uuid, stateMachineModel);
        if (peek.isInitial() || (!peek.isInitial() && !machineMap.containsKey(peek.getParent()))) {
            machineMap.put(peek.getParent(), machine);
        }
    }        
    ...
}

从上面的源码可以看出:当子状态机只有一个初始化状态时,不会通过RegionState去创建子状态机。

在同一个类中有个buildMachine方法如下,增加一段"else if"代码后解决问题:

private StateMachine<S, E> buildMachine(Map<Object, StateMachine<S, E>> machineMap, Map<S, State<S, E>> stateMap,
			Map<S, StateHolder<S, E>> holderMap, Collection<StateData<S, E>> stateDatas, Collection<TransitionData<S, E>> transitionsData,
			BeanFactory beanFactory, Boolean contextEvents, DefaultExtendedState defaultExtendedState,
			TransitionsData<S, E> stateMachineTransitions, TaskExecutor taskExecutor, TaskScheduler taskScheduler, String machineId,
			UUID uuid, StateMachineModel<S, E> stateMachineModel)
			
	...
	// 如果当前状态是虚拟状态,而且类型是JOIN
	} else if (stateData.getPseudoStateKind() == PseudoStateKind.JOIN) {
		S s = stateData.getState();
		List<S> list = stateMachineTransitions.getJoins().get(s);
		List<State<S, E>> joins = new ArrayList<State<S,E>>();

		// if join source is a regionstate, get
		// it's end states from regions
		if (list.size() == 1) {
			State<S, E> ss1 = stateMap.get(list.get(0));
			// 判断状态是RegionState时,才会把子状态机的终结状态加入到joins中
			if (ss1 instanceof RegionState) { 
				Collection<Region<S, E>> regions = ((RegionState<S, E>)ss1).getRegions();
				for (Region<S, E> r : regions) {
					Collection<State<S, E>> ss2 = r.getStates();
					for (State<S, E> ss3 : ss2) {
						if (ss3.getPseudoState() != null && ss3.getPseudoState().getKind() == PseudoStateKind.END) {
							joins.add(ss3);
							continue;
						}
					}
				}
			// 下面这个else if是我加的代码
			// 判断如果当前状态是子状态机状态类型
			// 也把子状态的终结状态加入到joins中
			} else if (ss1 instanceof StateMachineState) {
			    Collection<State<S, E>> subStates = ((StateMachineState) ss1).getSubmachine().getStates();
			    for (State<S, E> subState : subStates) {
			        if (subState.getPseudoState() != null && subState.getPseudoState().getKind() == PseudoStateKind.END) {
			            joins.add(subState);
			        }
			    }
            }
		} else {
			for (S fs : list) {
				joins.add(stateMap.get(fs));
			}
		}

		List<JoinStateData<S, E>> joinTargets = new ArrayList<JoinStateData<S, E>>();
		Collection<TransitionData<S, E>> transitions = stateMachineTransitions.getTransitions();
		for (TransitionData<S, E> tt : transitions) {
			if (tt.getSource() == s) {
				StateHolder<S, E> holder = new StateHolder<S, E>(stateMap.get(tt.getTarget()));
				if (holder.getState() == null) {
					holderMap.put(tt.getTarget(), holder);
				}
				joinTargets.add(new JoinStateData<S, E>(holder, tt.getGuard()));
			}
		}
		JoinPseudoState<S, E> pseudoState = new JoinPseudoState<S, E>(joins, joinTargets);

		state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(),
				stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel);
		states.add(state);
		stateMap.put(stateData.getState(), state);
	}
	...
}

这样就能解决Fork+Join+单个Region无法join的问题了

Github issue: https://github.com/spring-projects/spring-statemachine/issues/337

原文地址:https://www.cnblogs.com/lanhj/p/6617254.html