Fix use of multiple EnableStateMachineFactory
- Fixing internal handling of multiple EnableStateMachineFactory adapters which were wrongly mixed up during internal config build. - Techically we can have multiple adapters building same state machine and same factory if carefully crafted. However a check was missing in StateMachineFactoryConfiguration to only attempt configure builder for exact attached adapter, thus causing same internal builder called twice which then failed because build was already called. This was originally done for StateMachineConfiguration but missed for StateMachineFactoryConfiguration. - Fixes #117
This commit is contained in:
@@ -40,6 +40,7 @@ import org.springframework.statemachine.config.builders.StateMachineStates;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions;
|
||||
import org.springframework.statemachine.config.common.annotation.AbstractImportingAnnotationConfiguration;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurer;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@Configuration
|
||||
public class StateMachineFactoryConfiguration<S extends Enum<S>, E extends Enum<E>> extends
|
||||
@@ -56,6 +57,7 @@ public class StateMachineFactoryConfiguration<S extends Enum<S>, E extends Enum<
|
||||
EnableStateMachineFactory.class.getName(), false));
|
||||
Boolean contextEvents = attributes.getBoolean("contextEvents");
|
||||
beanDefinitionBuilder.addConstructorArgValue(builder);
|
||||
beanDefinitionBuilder.addConstructorArgValue(importingClassMetadata.getClassName());
|
||||
beanDefinitionBuilder.addConstructorArgValue(contextEvents);
|
||||
return beanDefinitionBuilder.getBeanDefinition();
|
||||
}
|
||||
@@ -72,18 +74,16 @@ public class StateMachineFactoryConfiguration<S extends Enum<S>, E extends Enum<
|
||||
FactoryBean<StateMachineFactory<S, E>>, BeanFactoryAware, InitializingBean {
|
||||
|
||||
private final StateMachineConfigBuilder<S, E> builder;
|
||||
|
||||
private List<AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>>> configurers;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private StateMachineFactory<S, E> stateMachineFactory;
|
||||
|
||||
private String clazzName;
|
||||
private Boolean contextEvents;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public StateMachineFactoryDelegatingFactoryBean(StateMachineConfigBuilder<S, E> builder, Boolean contextEvents) {
|
||||
public StateMachineFactoryDelegatingFactoryBean(StateMachineConfigBuilder<S, E> builder, String clazzName, Boolean contextEvents) {
|
||||
this.builder = builder;
|
||||
this.clazzName = clazzName;
|
||||
this.contextEvents = contextEvents;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,10 @@ public class StateMachineFactoryConfiguration<S extends Enum<S>, E extends Enum<
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
for (AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>> configurer : configurers) {
|
||||
builder.apply(configurer);
|
||||
Class<?> clazz = configurer.getClass();
|
||||
if (ClassUtils.getUserClass(clazz).getName().equals(clazzName)) {
|
||||
builder.apply(configurer);
|
||||
}
|
||||
}
|
||||
StateMachineConfig<S, E> stateMachineConfig = builder.getOrBuild();
|
||||
StateMachineTransitions<S, E> stateMachineTransitions = stateMachineConfig.getTransitions();
|
||||
|
||||
@@ -86,6 +86,38 @@ public class StateMachineFactoryTests extends AbstractStateMachineTests {
|
||||
assertThat(((SmartLifecycle)machine).isRunning(), is(false));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Test
|
||||
public void testCustomNamedFactory() {
|
||||
context.register(Config4.class);
|
||||
context.refresh();
|
||||
StateMachineFactory<TestStates, TestEvents> stateMachineFactory =
|
||||
context.getBean("factory1", ObjectStateMachineFactory.class);
|
||||
StateMachine<TestStates,TestEvents> machine = stateMachineFactory.getStateMachine();
|
||||
machine.start();
|
||||
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Test
|
||||
public void testMultipleCustomNamedFactories() {
|
||||
context.register(Config4.class, Config5.class);
|
||||
context.refresh();
|
||||
StateMachineFactory<TestStates, TestEvents> stateMachineFactory1 =
|
||||
context.getBean("factory1", ObjectStateMachineFactory.class);
|
||||
StateMachineFactory<TestStates, TestEvents> stateMachineFactory2 =
|
||||
context.getBean("factory2", ObjectStateMachineFactory.class);
|
||||
StateMachine<TestStates,TestEvents> machine1 = stateMachineFactory1.getStateMachine();
|
||||
StateMachine<TestStates,TestEvents> machine2 = stateMachineFactory2.getStateMachine();
|
||||
|
||||
machine1.start();
|
||||
machine2.start();
|
||||
|
||||
assertThat(machine1.getState().getIds(), contains(TestStates.S1));
|
||||
assertThat(machine2.getState().getIds(), contains(TestStates.S1));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachineFactory
|
||||
static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
@@ -157,4 +189,64 @@ public class StateMachineFactoryTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachineFactory(name = "factory1")
|
||||
public static class Config4 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineConfigurationConfigurer<TestStates, TestEvents> config) throws Exception {
|
||||
config
|
||||
.withConfiguration()
|
||||
.autoStartup(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.states(EnumSet.allOf(TestStates.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachineFactory(name = "factory2")
|
||||
public static class Config5 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineConfigurationConfigurer<TestStates, TestEvents> config) throws Exception {
|
||||
config
|
||||
.withConfiguration()
|
||||
.autoStartup(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.states(EnumSet.allOf(TestStates.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user