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= ...
随机推荐
- eclipse用tomcat发布网站的目录
用eclipse添加的tomcat发布网站时,在tomcat安装目录中的webapps时找不到发布的网站.这是由于eclipse的默认配置,把项目发布到别的文件夹中了.如果想发布到webapps里面, ...
- HDU 1010 Tempter of the Bone【DFS】
学习剪枝的第一篇@_@学习别人的剪枝,一剪就是两天@_@---- 参看的这篇--http://blog.csdn.net/libin56842/article/details/8962512自己的小体 ...
- ADO.NET之断开数据连接的数据库操作
在ADO.NET对数据库操作时有两种方式一种时与数据库实时连接,第二种时断开连接的操作. 断开连接的操作使用SqlDataAdapter来实现,我们要把数据库中的表数据加载到winform中的data ...
- sublime text3前端常用插件
安装Package Control 在安装插件之前,需要让sublime安装Package Control.打开Sublime Text的控制台,快捷键ctrl + ~,在控制台中输入以下代码. im ...
- swift语言点评十二-Subscripts
Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the m ...
- SpringCloud学习笔记(3)----Spring Cloud Netflix之深入理解Eureka
1. Eureka服务端的启动过程 1.1 入口类EurekaServerInitializerConfiguration类, public void start() { (new Thread(n ...
- 利用SignalR来同步更新Winfrom
之前写了个用Socket来更新多个Winfrom的试例,这两天看了下SignalR,也用这个来试一下 SignalR 地址:https://www.asp.net/signalr 我这个也是基于 ht ...
- 移动端mete设置
<!DOCTYPE html> <!-- 使用 HTML5 doctype,不区分大小写 --> <html lang="zh-cmn-Hans"&g ...
- 利用MFC创建窗口、消息映射、window中的字节
利用MFC创建窗口: 1.mfc的头文件:afxwin.h 2.自定义类,继承于CWinApp,应用程序类(app应用程序对象,有且仅有一个) 3.程序入口:Initinstance 4.在程序入口中 ...
- [Debug]SpaceVim中neomake报错 Error while trying to load a compilation database
回家装上archlinux,突发奇想装个SpaceVim写题 安装配置一路可以说是没有太大问题 最后在写题时出现如下问题 Error while trying to load a compilatio ...