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:
Janne Valkealahti
2015-11-05 19:31:17 +00:00
parent 58e9f01aaf
commit d55a7ab802
2 changed files with 101 additions and 6 deletions

View File

@@ -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();

View File

@@ -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);
}
}
}