我正在尝试用spring-statemachine构建一个分层状态机。它应该有两个正交的状态,每个状态代表两个服务的状态。为了简单起见,下面的代码减少了状态数,但仍然会发生相同的错误。
public enum MachineState {
BUFF,BUFF_OFFLINE, BUFF_ONLINE,
CB,CB_OFFLINE,CB_ONLINE
}
public enum MachineEvent {
BUFF_OFF,BUFF_ON,
CB_OFF, CB_NORESP, BUFF_NORESP, CB_ON
}
@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<MachineState, MachineEvent> {
@Override
public void configure(final StateMachineConfigurationConfigurer<MachineState, MachineEvent> config)
throws Exception {
config
.withConfiguration()
.autoStartup(true);
}
@Override
public void configure(final StateMachineStateConfigurer<MachineState, MachineEvent> states)
throws Exception {
states
.withStates()
.initial(MachineState.BUFF)
.and()
.withStates()
.parent(MachineState.BUFF)
.initial(MachineState.BUFF_OFFLINE)
.state(MachineState.BUFF_ONLINE)
.and()
.withStates()
.initial(MachineState.CB)
.and()
.withStates()
.parent(MachineState.CB)
.initial(MachineState.CB_OFFLINE)
.state(MachineState.CB_ONLINE)
.and()
;
}
@Override
public void configure(final StateMachineTransitionConfigurer<MachineState, MachineEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(MachineState.BUFF_OFFLINE).target(MachineState.BUFF_ONLINE)
.event(MachineEvent.BUFF_ON)
.and()
.withExternal()
.source(MachineState.BUFF_ONLINE).target(MachineState.BUFF_OFFLINE)
.event(MachineEvent.BUFF_OFF)
.and()
.withExternal()
.source(MachineState.CB_OFFLINE).target(MachineState.CB_ONLINE)
.event(MachineEvent.CB_ON)
.and()
.withExternal()
.source(MachineState.CB_ONLINE).target(MachineState.CB_OFFLINE)
.event(MachineEvent.CB_OFF)
.and()
.withInternal()
.source(MachineState.CB)
.event(MachineEvent.CB_NORESP)
.and()
.withInternal()
.source(MachineState.BUFF)
.event(MachineEvent.BUFF_NORESP)
.and()
;
}
}首先,我在配置中做错了什么吗?
我得到的错误如下
Caused by: java.lang.IllegalArgumentException: Source must be set
at org.springframework.util.Assert.notNull(Assert.java:115) ~[spring-core-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.statemachine.transition.AbstractTransition.<init>(AbstractTransition.java:63) ~[spring-statemachine-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
at org.springframework.statemachine.transition.AbstractInternalTransition.<init>(AbstractInternalTransition.java:35) ~[spring-statemachine-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
at org.springframework.statemachine.transition.DefaultInternalTransition.<init>(DefaultInternalTransition.java:35) ~[spring-statemachine-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
at org.springframework.statemachine.config.AbstractStateMachineFactory.buildMachine(AbstractStateMachineFactory.java:704) ~[spring-statemachine-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
at org.springframework.statemachine.config.AbstractStateMachineFactory.getStateMachine(AbstractStateMachineFactory.java:189) ~[spring-statemachine-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
at org.springframework.statemachine.config.AbstractStateMachineFactory.getStateMachine(AbstractStateMachineFactory.java:126) ~[spring-statemachine-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
at org.springframework.statemachine.config.configuration.StateMachineConfiguration$StateMachineDelegatingFactoryBean.afterPropertiesSet(StateMachineConfiguration.java:154) ~[spring-statemachine-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]我对应用程序进行了调试,发现在spring-statemachine core AbstractStateMachineFactory buildMachine()中,stateMap缺少CB和BUFF状态之一。最奇怪的部分是看起来是随机的,有时它实际上包含了整个集合,我也不例外。
我尝试删除这两个内部转换并调试代码,发现即使stateMap是不完整的(如果我从那个缺失的状态转换过来,它就会失败),实例化后的状态机看起来就像我希望的那样,所有状态都在那里。
有什么想法吗?
示例项目https://www.dropbox.com/s/qlarppnma0dq9ai/statemachineerror.tar.gz?dl=0
发布于 2016-09-01 22:12:57
因此,为了实现我想要的,我不应该使用内部转换,而应该使用外部转换。这是一个可以在任何子状态上发生的事件,并且应该将区域返回到初始状态。
.withExternal()
.source(MachineState.CB).target(MachineState.CB)
.event(MachineEvent.CB_NORESP)
.and()
.withExternal()
.source(MachineState.BUFF).target(MachineState.BUFF)
.event(MachineEvent.BUFF_NORESP)
.and()不过,如果内部转换是可行的,我也找到了一个解决办法。这个bug的局限性是,如果你想像我一样使用内部转换,你就不能在初始状态下拥有正交区域。解决方案是引入两个新状态,并自动从第一个状态转换到第二个状态,并使第二个状态成为两个正交区域的父状态。
@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<MachineState, MachineEvent> {
@Override
public void configure(final StateMachineConfigurationConfigurer<MachineState, MachineEvent> config)
throws Exception {
config
.withConfiguration()
.autoStartup(true);
}
@Override
public void configure(final StateMachineStateConfigurer<MachineState, MachineEvent> states)
throws Exception {
states
.withStates()
.initial(MachineState.INITIAL)
.state(MachineState.INITIAL, init(), null)
.state(MachineState.PARENT)
.and()
// Region 1 (BUFF)
.withStates()
.parent(MachineState.PARENT)
.initial(MachineState.BUFF)
.and()
.withStates()
.parent(MachineState.BUFF)
.initial(MachineState.BUFF_OFFLINE)
.state(MachineState.BUFF_ONLINE)
.and()
// Region 2 (CB)
.withStates()
.parent(MachineState.PARENT)
.initial(MachineState.CB)
.and()
.withStates()
.parent(MachineState.CB)
.initial(MachineState.CB_OFFLINE)
.state(MachineState.CB_ONLINE)
.and()
;
}
@Override
public void configure(final StateMachineTransitionConfigurer<MachineState, MachineEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(MachineState.BUFF_OFFLINE).target(MachineState.BUFF_ONLINE)
.event(MachineEvent.BUFF_ON)
.and()
.withExternal()
.source(MachineState.BUFF_ONLINE).target(MachineState.BUFF_OFFLINE)
.event(MachineEvent.BUFF_OFF)
.and()
.withExternal()
.source(MachineState.CB_OFFLINE).target(MachineState.CB_ONLINE)
.event(MachineEvent.CB_ON)
.and()
.withExternal()
.source(MachineState.CB_ONLINE).target(MachineState.CB_OFFLINE)
.event(MachineEvent.CB_OFF)
.and()
.withInternal()
.source(MachineState.CB)
.event(MachineEvent.CB_NORESP)
.and()
.withInternal()
.source(MachineState.BUFF)
.event(MachineEvent.BUFF_NORESP)
.and()
.withExternal()
.source(MachineState.INITIAL).target(MachineState.PARENT)
.event(MachineEvent.INIT)
.and()
;
}
@Bean
public Action<MachineState, MachineEvent> init() {
return new Action<MachineState, MachineEvent>() {
@Override
public void execute(StateContext<MachineState, MachineEvent> context) {
context.getStateMachine().sendEvent(MachineEvent.INIT);
}
};
}
}https://stackoverflow.com/questions/39149000
复制相似问题