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状态的问题的更多相关文章

  1. spring cloud教程之使用spring boot创建一个应用

    <7天学会spring cloud>第一天,熟悉spring boot,并使用spring boot创建一个应用. Spring Boot是Spring团队推出的新框架,它所使用的核心技术 ...

  2. 如何在github上fork一个项目来贡献代码以及同步原作者的修改

    [-] 如何贡献自己的力量 如何让自己的项目与原作者的项目保持同步 作为一个IT人,通过github进行学习是最快的成长手 段.我们可以浏览别人的优秀代码.但只看不动手还是成长得很慢,因此为别人贡献代 ...

  3. Spring Boot实现一个监听用户请求的拦截器

    项目中需要监听用户具体的请求操作,便通过一个拦截器来监听,并继续相应的日志记录 项目构建与Spring Boot,Spring Boot实现一个拦截器很容易. Spring Boot的核心启动类继承W ...

  4. XI.spring的点点滴滴--IObjectFactoryPostProcessor(工厂后处理器)

    承接上文 IObjectFactoryPostProcessor(工厂后处理器)) 前提是实现接口的对象注册给当前容器 直接继承的对象调用这个postProcessBeanFactory方法,参数为工 ...

  5. 【转】如何在github上fork一个项目来贡献代码以及同步原作者的修改 -- 不错

    原文网址:http://www.cnblogs.com/astwish/articles/3548844.html 作为一个IT人,通过github进行学习是最快的成长手段.我们可以浏览别人的优秀代码 ...

  6. Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)

    应用场景 之前我们已经通过<Spring Cloud Stream消费失败后的处理策略(一):自动重试>一文介绍了Spring Cloud Stream默认的消息重试功能.本文将介绍Rab ...

  7. Spring Cloud Stream消费失败后的处理策略(二):自定义错误处理逻辑

    应用场景 上一篇<Spring Cloud Stream消费失败后的处理策略(一):自动重试>介绍了默认就会生效的消息重试功能.对于一些因环境原因.网络抖动等不稳定因素引发的问题可以起到比 ...

  8. GitHub上fork一个项目贡献代码以及同步原作者的修改【转】

    如何贡献自己的力量 首先你总得有自己的github帐号吧,注册一个,非常简单,只需用户名,邮箱,密码,邮箱只是用来找回密码的,不做验证.因此注册后立即能用!比如我现在新注册一个叫JsLouvre的示范 ...

  9. spring cloud 创建一个简单Eureka Server

    在Spring Cloud实现一个Eureka Server是一件非常简单的事情.下面我们来写一个Eureka Server DEMO. 编码 父项目pom.xml <?xml version= ...

随机推荐

  1. activity的23张表

    --二进制数据表 SELECT * FROM act_ge_bytearray; --属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录, SELECT * FROM act_g ...

  2. form表单里的坑

    我们在写前端表单页面的时候,为了更好的SEO,我们会使用form标签,但是我们经常的情况是:我们并不需要form标签的一些默认事件,比如: 1.form内只有一个input标签的话,回车会触发表单的提 ...

  3. (转)String StringBuilder StringBuffer 对比 总结得非常好

    来源:http://blog.csdn.net/clam_clam/article/details/6831345 转自:http://www.iteye.com/topic/522167 作者:每次 ...

  4. dedecms如何把时间戳转换成正常时间格式

    如果在datalist 可以用{dede:field.shijian function=strftime('%Y-%m-%d',@me)/} 如果不在datalist中调用的话,单独调用用<?p ...

  5. 影像服务——加载CESIUM自带的影像服务

    1.加载arcgis数据——ArcGisMapServerImageryProvider var viewer = new Cesium.Viewer("cesiumDiv",{ ...

  6. ansible搭建mysql主主模式

    ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet.cfengine.chef.func.fabric)等优点,实现了批量系统配置.批量程序部署.批量运行命 ...

  7. 【BZOJ 1177】 [Apio2009]Oil

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 如上图. 显然如果三个正方形.只可能是上面的情况. 则可以处理一下左上角.右上角.左下角.右下角的前缀最大正方形(dp),以及以某一 ...

  8. Openstack API 开发 快速入门

    Openstack 做为流行的开源云计算平台,其最大特性是利用其提供的基础设施API,让我们可以以软件的方式来动态管理IAAS资源.Openstack 提供的api是流行的Rest API.     ...

  9. 作为一名Android APP开发者的自我总结

    每当接近年尾,最痛苦的工作无疑是写年终总结,写总结的同时不禁感叹这一年过得不容易阿.突然想起这一年也是自己开发Android APP的第一年,于是觉得应该给自己的APP来一个年终总结. 一.开发方面严 ...

  10. drupal7 怎样将一个date字段加入上日期插件效果

    //这里以created字段为样例 function Hook_form_alter($form,$form_state,$form_id){ $form['created']['#type'] = ...