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
Spring-statemachine fork一个region后不能进入join状态的问题的更多相关文章
- spring cloud教程之使用spring boot创建一个应用
<7天学会spring cloud>第一天,熟悉spring boot,并使用spring boot创建一个应用. Spring Boot是Spring团队推出的新框架,它所使用的核心技术 ...
- 如何在github上fork一个项目来贡献代码以及同步原作者的修改
[-] 如何贡献自己的力量 如何让自己的项目与原作者的项目保持同步 作为一个IT人,通过github进行学习是最快的成长手 段.我们可以浏览别人的优秀代码.但只看不动手还是成长得很慢,因此为别人贡献代 ...
- Spring Boot实现一个监听用户请求的拦截器
项目中需要监听用户具体的请求操作,便通过一个拦截器来监听,并继续相应的日志记录 项目构建与Spring Boot,Spring Boot实现一个拦截器很容易. Spring Boot的核心启动类继承W ...
- XI.spring的点点滴滴--IObjectFactoryPostProcessor(工厂后处理器)
承接上文 IObjectFactoryPostProcessor(工厂后处理器)) 前提是实现接口的对象注册给当前容器 直接继承的对象调用这个postProcessBeanFactory方法,参数为工 ...
- 【转】如何在github上fork一个项目来贡献代码以及同步原作者的修改 -- 不错
原文网址:http://www.cnblogs.com/astwish/articles/3548844.html 作为一个IT人,通过github进行学习是最快的成长手段.我们可以浏览别人的优秀代码 ...
- Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)
应用场景 之前我们已经通过<Spring Cloud Stream消费失败后的处理策略(一):自动重试>一文介绍了Spring Cloud Stream默认的消息重试功能.本文将介绍Rab ...
- Spring Cloud Stream消费失败后的处理策略(二):自定义错误处理逻辑
应用场景 上一篇<Spring Cloud Stream消费失败后的处理策略(一):自动重试>介绍了默认就会生效的消息重试功能.对于一些因环境原因.网络抖动等不稳定因素引发的问题可以起到比 ...
- GitHub上fork一个项目贡献代码以及同步原作者的修改【转】
如何贡献自己的力量 首先你总得有自己的github帐号吧,注册一个,非常简单,只需用户名,邮箱,密码,邮箱只是用来找回密码的,不做验证.因此注册后立即能用!比如我现在新注册一个叫JsLouvre的示范 ...
- spring cloud 创建一个简单Eureka Server
在Spring Cloud实现一个Eureka Server是一件非常简单的事情.下面我们来写一个Eureka Server DEMO. 编码 父项目pom.xml <?xml version= ...
随机推荐
- 读 Real-Time Rendering 收获 - chapter 6. texturing
Texturing, at its simplest, is a techinique for efficiently modeling the surface's properties.
- Ubuntu系统下Import cv2提示no modules ...错误
最近利用pycharm在Ubuntu系统下调试一个Python项目,将pycharm的解释器从python2.7更换到python3.4后,程序中的Import cv2提示no modules nam ...
- UVa 10801 Lift Hopping【floyd 】
题意:给出n个电梯,每个电梯的运行时间,每个电梯只能在相应的楼层停靠,而且没有楼梯,再给出想去的楼层,问从0层能否到达想去的楼层,求到达的最短时间 建图还是没有建出来--- 因为n<100,可以 ...
- Django------->>>modle
import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "modletest.settings") ...
- Hadoop-2.4.1 ubuntu集群安装配置教程
一.环境 系统: Ubuntu 14.04 32bit Hadoop版本: Hadoop 2.4.1 (stable) JDK版本: 1.7 集群数量:3台 注意事项:我们从Apache官方网站下载的 ...
- javascript的var声明变量和不用var声明变量在全局作用域的区别;
在全局作用域下,使用var定义的变量不可以delete,没有var 定义的变量可以delete.也就说明隐含全局变量严格来说不是真正的变量,而是全局对象的属性,因为属性可以通过delete删除,而变量 ...
- vue之computed和watch
计算属性 computed 侦听器or观察者 watch 一直以来对computed和watch一知半解,用的时候就迷迷糊糊的,今天仔细看了看文档,突然茅塞顿开,原来就是这么简单啊: computed ...
- 【codeforces 340B】Maximal Area Quadrilateral
[题目链接]:http://codeforces.com/problemset/problem/340/B [题意] 给你n个点,让你在这里面找4个点构成一个四边形; 求出最大四边形的面积; [题解] ...
- Java基础学习总结(3)——抽象类
一.抽象类介绍 下面通过一下的小程序深入理解抽象类 因此在类Animal里面只需要定义这个enjoy()方法就可以了,使用abstract关键字把enjoy()方法定义成一个抽象方法,定义如下:pub ...
- 【转】Hook钩子C#实例
[转]Hook钩子C#实例 转过来的文章,出处已经不知道了,但只这篇步骤比较清晰,就贴出来了. 一.写在最前 本文的内容只想以最通俗的语言说明钩子的使用方法,具体到钩子的详细介绍可以参照下面的网址: ...