zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    MySQL的Date()函数拼接
    org.osgi.framework.BundleException: Exception in org.eclipse.core.resources.ResourcesPlugin.start()
    js判断对象是否为空对象的几种方法
    json,js中typeof用法详细介绍及NaN、 null 及 undefined 的区别
    将[object Object]转换成json对象
    升级d7的代码到2010以上版本注意事项(SetLength的参数就是字符长度,而不是字节长度,但Move函数要改)
    我是如何用 10 天自学编程,改变一生的?(学习编程的时候,不要死记硬背,要培养感觉)
    Anbox —— 在 Linux 系统中运行 Android 应用
    一定要在commit之前做RAR备份,这样在出问题的时候,可以排除别人代码的干扰
    排序算法总结
  • 原文地址:https://www.cnblogs.com/lanhj/p/6617254.html
Copyright © 2011-2022 走看看