Add base support for eclipse papyrus modeling

- First stage to add support for UI modeling via
  eclipse uml2 and papyrus frameworks.
- Add new interface StateMachineModelFactory which is
  a central point of building machines models outside
  of a normal annotation/build classes. What this mean is
  a hook to config model so that machine can be defined
  via a model factory, instead of writing config based on
  normal adapter callbacks.
- Integrate model config into an annotation model so that
  you can still use normal adapters but point those to
  model factory classes, which then allows external hooks
  for machine configuration.
- New spring-statemachine-uml package which supports
  eclipse uml2/emf frameworks to define a machine config.
- Relates to #193
This commit is contained in:
Janne Valkealahti
2016-04-09 16:04:16 +01:00
parent 6cce27e30b
commit 51145f1368
31 changed files with 1842 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ import org.springframework.statemachine.config.builders.StateMachineConfigBuilde
import org.springframework.statemachine.config.builders.StateMachineConfigurationBuilder;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineConfigurer;
import org.springframework.statemachine.config.builders.StateMachineModelBuilder;
import org.springframework.statemachine.config.builders.StateMachineModelConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateBuilder;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
@@ -36,12 +38,14 @@ import org.springframework.statemachine.config.common.annotation.ObjectPostProce
*/
public abstract class AbstractStateMachineConfigurerAdapter<S, E> implements StateMachineConfigurer<S, E> {
private StateMachineModelBuilder<S, E> modelBuilder;
private StateMachineTransitionBuilder<S, E> transitionBuilder;
private StateMachineStateBuilder<S, E> stateBuilder;
private StateMachineConfigurationBuilder<S, E> configurationBuilder;
@Override
public final void init(StateMachineConfigBuilder<S, E> config) throws Exception {
config.setSharedObject(StateMachineModelBuilder.class, getStateMachineModelBuilder());
config.setSharedObject(StateMachineTransitionBuilder.class, getStateMachineTransitionBuilder());
config.setSharedObject(StateMachineStateBuilder.class, getStateMachineStateBuilder());
config.setSharedObject(StateMachineConfigurationBuilder.class, getStateMachineConfigurationBuilder());
@@ -51,6 +55,10 @@ public abstract class AbstractStateMachineConfigurerAdapter<S, E> implements Sta
public void configure(StateMachineConfigBuilder<S, E> config) throws Exception {
}
@Override
public void configure(StateMachineModelConfigurer<S, E> model) throws Exception {
}
@Override
public void configure(StateMachineConfigurationConfigurer<S, E> config) throws Exception {
}
@@ -68,6 +76,15 @@ public abstract class AbstractStateMachineConfigurerAdapter<S, E> implements Sta
return builder instanceof StateMachineConfigBuilder;
}
protected final StateMachineModelBuilder<S, E> getStateMachineModelBuilder() throws Exception {
if (modelBuilder != null) {
return modelBuilder;
}
modelBuilder = new StateMachineModelBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
configure(modelBuilder);
return modelBuilder;
}
protected final StateMachineTransitionBuilder<S, E> getStateMachineTransitionBuilder() throws Exception {
if (transitionBuilder != null) {
return transitionBuilder;

View File

@@ -23,6 +23,8 @@ import org.springframework.statemachine.config.builders.StateMachineConfigBuilde
import org.springframework.statemachine.config.builders.StateMachineConfigurationBuilder;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineConfigurer;
import org.springframework.statemachine.config.builders.StateMachineModelBuilder;
import org.springframework.statemachine.config.builders.StateMachineModelConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateBuilder;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
@@ -75,6 +77,15 @@ public class StateMachineBuilder {
builder = new StateMachineConfigBuilder<S, E>();
}
/**
* Configure model.
*
* @return the state machine model configurer
*/
public StateMachineModelConfigurer<S, E> configureModel() {
return adapter.modelBuilder;
}
/**
* Configure configuration.
*
@@ -144,12 +155,14 @@ public class StateMachineBuilder {
private static class BuilderStateMachineConfigurerAdapter<S extends Object, E extends Object>
implements StateMachineConfigurer<S, E> {
private StateMachineModelBuilder<S, E> modelBuilder;
private StateMachineTransitionBuilder<S, E> transitionBuilder;
private StateMachineStateBuilder<S, E> stateBuilder;
private StateMachineConfigurationBuilder<S, E> configurationBuilder;
BuilderStateMachineConfigurerAdapter() {
try {
getStateMachineModelBuilder();
getStateMachineTransitionBuilder();
getStateMachineStateBuilder();
getStateMachineConfigurationBuilder();
@@ -160,6 +173,7 @@ public class StateMachineBuilder {
@Override
public void init(StateMachineConfigBuilder<S, E> config) throws Exception {
config.setSharedObject(StateMachineModelBuilder.class, getStateMachineModelBuilder());
config.setSharedObject(StateMachineTransitionBuilder.class, getStateMachineTransitionBuilder());
config.setSharedObject(StateMachineStateBuilder.class, getStateMachineStateBuilder());
config.setSharedObject(StateMachineConfigurationBuilder.class, getStateMachineConfigurationBuilder());
@@ -174,6 +188,10 @@ public class StateMachineBuilder {
return false;
}
@Override
public void configure(StateMachineModelConfigurer<S, E> model) throws Exception {
}
@Override
public void configure(StateMachineConfigurationConfigurer<S, E> config) throws Exception {
}
@@ -186,6 +204,15 @@ public class StateMachineBuilder {
public void configure(StateMachineTransitionConfigurer<S, E> transitions) throws Exception {
}
protected final StateMachineModelBuilder<S, E> getStateMachineModelBuilder() throws Exception {
if (modelBuilder != null) {
return modelBuilder;
}
modelBuilder = new StateMachineModelBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
configure(modelBuilder);
return modelBuilder;
}
protected final StateMachineTransitionBuilder<S, E> getStateMachineTransitionBuilder() throws Exception {
if (transitionBuilder != null) {
return transitionBuilder;
@@ -209,7 +236,5 @@ public class StateMachineBuilder {
configurationBuilder = new StateMachineConfigurationBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
return configurationBuilder;
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.builders;
import org.springframework.statemachine.config.configurers.ModelConfigurer;
import org.springframework.statemachine.config.model.StateMachineModelFactory;
/**
* Data object used return and build data from a {@link ModelConfigurer}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class ModelData<S, E> {
private StateMachineModelFactory<S, E> factory;
/**
* Instantiates a new model data.
*
* @param factory the factory
*/
public ModelData(StateMachineModelFactory<S, E> factory) {
this.factory = factory;
}
/**
* Gets the factory.
*
* @return the factory
*/
public StateMachineModelFactory<S, E> getFactory() {
return factory;
}
}

View File

@@ -17,10 +17,21 @@ package org.springframework.statemachine.config.builders;
import org.springframework.statemachine.config.StateMachineConfig;
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
import org.springframework.statemachine.config.model.ConfigurationData;
import org.springframework.statemachine.config.model.StateMachineModel;
import org.springframework.statemachine.config.model.StatesData;
import org.springframework.statemachine.config.model.TransitionsData;
/**
* {@link AnnotationBuilder} handling all shared builders which effectively contructs
* full configuration for {@link StateMachineConfig}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class StateMachineConfigBuilder<S, E>
extends
AbstractConfiguredAnnotationBuilder<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>, StateMachineConfigBuilder<S, E>> {
@@ -28,17 +39,30 @@ public class StateMachineConfigBuilder<S, E>
@SuppressWarnings("unchecked")
@Override
protected StateMachineConfig<S, E> performBuild() throws Exception {
// TODO: should prevent calling state/transition builder if model is given
StateMachineModelBuilder<?, ?> modelBuilder = getSharedObject(StateMachineModelBuilder.class);
StateMachineConfigurationBuilder<?, ?> configurationBuilder = getSharedObject(StateMachineConfigurationBuilder.class);
StateMachineTransitionBuilder<?, ?> transitionBuilder = getSharedObject(StateMachineTransitionBuilder.class);
StateMachineStateBuilder<?, ?> stateBuilder = getSharedObject(StateMachineStateBuilder.class);
// build config first as it's shared with transition builder
ConfigurationData<S, E> config = (ConfigurationData<S, E>) configurationBuilder.build();
transitionBuilder.setSharedObject(ConfigurationData.class, config);
TransitionsData<S, E> transitions = (TransitionsData<S, E>) transitionBuilder.build();
StatesData<S, E> states = (StatesData<S, E>) stateBuilder.build();
StateMachineConfig<S, E> bean = new StateMachineConfig<S, E>(config, transitions, states);
return bean;
}
ModelData<S, E> model = (ModelData<S, E>) modelBuilder.build();
ConfigurationData<S, E> stateMachineConfigurationConfig = null;
TransitionsData<S, E> transitions = null;
StatesData<S, E> states = null;
if (model.getFactory() != null) {
StateMachineModel<S,E> stateMachineModel = model.getFactory().build();
transitions = stateMachineModel.getTransitionsData();
states = stateMachineModel.getStatesData();
}
stateMachineConfigurationConfig = (ConfigurationData<S, E>) configurationBuilder.build();
if (transitions == null) {
transitionBuilder.setSharedObject(ConfigurationData.class, stateMachineConfigurationConfig);
transitions = (TransitionsData<S, E>) transitionBuilder.build();
}
if (states == null) {
states = (StatesData<S, E>) stateBuilder.build();
}
return new StateMachineConfig<S, E>(stateMachineConfigurationConfig, transitions, states);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,14 @@ import org.springframework.statemachine.config.common.annotation.AnnotationConfi
public interface StateMachineConfigurer<S, E> extends
AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>> {
/**
* Callback for {@link StateMachineModelConfigurer}.
*
* @param model the {@link StateMachineModelConfigurer}
* @throws Exception if configuration error happens
*/
void configure(StateMachineModelConfigurer<S, E> model) throws Exception;
/**
* Callback for {@link StateMachineConfigurationConfigurer}.
*
@@ -52,5 +60,4 @@ public interface StateMachineConfigurer<S, E> extends
* @throws Exception if configuration error happens
*/
void configure(StateMachineTransitionConfigurer<S, E> transitions) throws Exception;
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.builders;
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
import org.springframework.statemachine.config.configurers.DefaultModelConfigurer;
import org.springframework.statemachine.config.configurers.ModelConfigurer;
import org.springframework.statemachine.config.model.StateMachineModelFactory;
/**
* {@link AnnotationBuilder} for {@link ModelData}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class StateMachineModelBuilder<S, E>
extends AbstractConfiguredAnnotationBuilder<ModelData<S, E>, StateMachineModelConfigurer<S, E>, StateMachineModelBuilder<S, E>>
implements StateMachineModelConfigurer<S, E> {
private StateMachineModelFactory<S, E> factory;
/**
* Instantiates a new state machine model builder.
*/
public StateMachineModelBuilder() {
super();
}
/**
* Instantiates a new state machine model builder.
*
* @param objectPostProcessor the object post processor
* @param allowConfigurersOfSameType the allow configurers of same type
*/
public StateMachineModelBuilder(ObjectPostProcessor<Object> objectPostProcessor,
boolean allowConfigurersOfSameType) {
super(objectPostProcessor, allowConfigurersOfSameType);
}
/**
* Instantiates a new state machine model builder.
*
* @param objectPostProcessor the object post processor
*/
public StateMachineModelBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor);
}
@Override
protected ModelData<S, E> performBuild() throws Exception {
return new ModelData<S, E>(factory);
}
@Override
public ModelConfigurer<S, E> withModel() throws Exception {
return apply(new DefaultModelConfigurer<S, E>());
}
public void setStateMachineModelFactory(StateMachineModelFactory<S, E> factory) {
this.factory = factory;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.builders;
import org.springframework.statemachine.config.configurers.ModelConfigurer;
/**
* Configurer interface exposing model.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface StateMachineModelConfigurer<S, E> {
/**
* Gets a configurer for model.
*
* @return {@link ModelConfigurer} for chaining
* @throws Exception if configuration error happens
*/
ModelConfigurer<S, E> withModel() throws Exception;
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.configurers;
import org.springframework.statemachine.config.builders.ModelData;
import org.springframework.statemachine.config.builders.StateMachineModelBuilder;
import org.springframework.statemachine.config.builders.StateMachineModelConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
import org.springframework.statemachine.config.model.StateMachineModelFactory;
/**
* Default implementation of a {@link ModelConfigurer}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class DefaultModelConfigurer<S, E>
extends AnnotationConfigurerAdapter<ModelData<S, E>, StateMachineModelConfigurer<S, E>, StateMachineModelBuilder<S, E>>
implements ModelConfigurer<S, E> {
private StateMachineModelFactory<S, E> factory;
@Override
public void configure(StateMachineModelBuilder<S, E> builder) throws Exception {
builder.setStateMachineModelFactory(factory);
}
@Override
public ModelConfigurer<S, E> factory(StateMachineModelFactory<S, E> factory) {
this.factory = factory;
return this;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.configurers;
import org.springframework.statemachine.config.builders.StateMachineModelConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
import org.springframework.statemachine.config.model.StateMachineModelFactory;
/**
* Base {@code ModelConfigurer} interface for configuring state machine model.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface ModelConfigurer<S, E> extends
AnnotationConfigurerBuilder<StateMachineModelConfigurer<S, E>> {
/**
* Specify a state machine model factory.
*
* @param factory the state machine model factory
* @return configurer for chaining
*/
ModelConfigurer<S, E> factory(StateMachineModelFactory<S, E> factory);
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.model;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.guard.Guard;
/**
* Base implementation of a {@link StateMachineModelFactory} providing
* some common grounds for various implementations.
*
* This factory is able to resolve {@link Action}s or {@link Guard}s either from
* a {@link BeanFactory} if knows, or from manually registered instances. Manually
* registered actions or guards are needed if those are not created as beans or if
* whole state machine is working outside of an application context.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public abstract class AbstractStateMachineModelFactory<S, E> implements StateMachineComponentResolver<S, E>, BeanFactoryAware {
private BeanFactory beanFactory;
private final Map<String, Action<S, E>> registeredActions = new HashMap<>();
private final Map<String, Guard<S, E>> registeredGuards = new HashMap<>();
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/**
* Register {@link Action} into factory with a given id.
*
* @param id the id
* @param action the action
*/
public void registerAction(String id, Action<S, E> action) {
registeredActions.put(id, action);
}
/**
* Register {@link Guard} into factory with a given id.
*
* @param id the id
* @param guard the guard
*/
public void registerGuard(String id, Guard<S, E> guard) {
registeredGuards.put(id, guard);
}
/**
* Gets the bean factory.
*
* @return the bean factory
*/
protected final BeanFactory getBeanFactory() {
return beanFactory;
}
/**
* Resolve action.
*
* @param id the id
* @return the action
*/
@SuppressWarnings("unchecked")
public Action<S, E> resolveAction(String id) {
Action<S, E> a = null;
a = registeredActions.get(id);
if (a == null && beanFactory != null) {
a = beanFactory.getBean(id, Action.class);
}
if (a == null) {
throw new RuntimeException("Can't resolve action with id " + id + " either from registered actions nor beanfactory");
}
return a;
}
/**
* Resolve guard.
*
* @param id the id
* @return the guard
*/
@SuppressWarnings("unchecked")
public Guard<S, E> resolveGuard(String id) {
Guard<S, E> g = null;
g = registeredGuards.get(id);
if (g == null && beanFactory != null) {
g = beanFactory.getBean(id, Guard.class);
}
if (g == null) {
throw new RuntimeException("Can't resolve guard with id " + id + " either from registered guards nor beanfactory");
}
return g;
}
}

View File

@@ -64,6 +64,18 @@ public class StateData<S, E> {
this(null, null, state, null, null, null, initial);
}
/**
* Instantiates a new state data.
*
* @param parent the parent
* @param region the region
* @param state the state
* @param initial the initial
*/
public StateData(Object parent, Object region, S state, boolean initial) {
this(parent, region, state, null, null, null, initial);
}
/**
* Instantiates a new state data.
*

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.model;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.guard.Guard;
/**
* Strategy interface for registering and resolving state machine
* components by their id's
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface StateMachineComponentResolver<S, E> {
/**
* Register {@link Action} into factory with a given id.
*
* @param id the id
* @param action the action
*/
void registerAction(String id, Action<S, E> action);
/**
* Register {@link Guard} into factory with a given id.
*
* @param id the id
* @param guard the guard
*/
void registerGuard(String id, Guard<S, E> guard);
/**
* Resolve action.
*
* @param id the id
* @return the action
*/
Action<S, E> resolveAction(String id);
/**
* Resolve guard.
*
* @param id the id
* @return the guard
*/
Guard<S, E> resolveGuard(String id);
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.model;
/**
* A generic builder interface for building {@link StateMachineModel}s.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface StateMachineModelFactory<S, E> {
/**
* Builds the state machine model.
*
* @return the state machine model
*/
StateMachineModel<S, E> build();
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.model;
import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.Collection;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.AbstractStateMachineTests;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.ObjectStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineModelConfigurer;
public class StateMachineModelFactoryTests extends AbstractStateMachineTests {
@Test
public void testResolvingFromContext() {
context.register(Config1.class);
context.refresh();
TestStateMachineModelFactory modelBuilder = new TestStateMachineModelFactory();
modelBuilder.setBeanFactory(context);
ObjectStateMachineFactory<String, String> factory = new ObjectStateMachineFactory<>(modelBuilder.build());
StateMachine<String,String> stateMachine = factory.getStateMachine();
stateMachine.start();
assertThat(stateMachine.getState().getIds(), contains("S1"));
stateMachine.sendEvent("E1");
assertThat(stateMachine.getState().getIds(), contains("S2"));
}
@Test
public void testFromAnnotationConfig() {
context.register(Config2.class);
context.refresh();
@SuppressWarnings("unchecked")
StateMachine<String, String> stateMachine = context.getBean(StateMachine.class);
stateMachine.start();
assertThat(stateMachine.getState().getIds(), contains("S1"));
stateMachine.sendEvent("E1");
assertThat(stateMachine.getState().getIds(), contains("S2"));
}
@Configuration
static class Config1 {
@Bean
public Action<String, String> action1() {
return new Action<String, String>() {
@Override
public void execute(StateContext<String, String> context) {
}
};
}
}
@Configuration
@EnableStateMachine
public static class Config2 extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
model
.withModel()
.factory(modelFactory());
}
@Bean
public StateMachineModelFactory<String, String> modelFactory() {
return new TestStateMachineModelFactory();
}
@Bean
public Action<String, String> action1() {
return new Action<String, String>() {
@Override
public void execute(StateContext<String, String> context) {
}
};
}
}
@SuppressWarnings("unchecked")
private static class TestStateMachineModelFactory implements StateMachineModelFactory<String, String>, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public StateMachineModel<String, String> build() {
Action<String, String> action1 = beanFactory.getBean("action1", Action.class);
Collection<Action<String, String>> s2Actions = new ArrayList<>();
s2Actions.add(action1);
ConfigurationData<String, String> configurationData = new ConfigurationData<>();
Collection<StateData<String, String>> stateData = new ArrayList<>();
stateData.add(new StateData<String, String>("S1", true));
stateData.add(new StateData<String, String>(null, null, "S2", null, s2Actions, null));
StatesData<String, String> statesData = new StatesData<>(stateData);
Collection<TransitionData<String, String>> transitionData = new ArrayList<>();
transitionData.add(new TransitionData<String, String>("S1", "S2", "E1"));
TransitionsData<String, String> transitionsData = new TransitionsData<>(transitionData);
StateMachineModel<String, String> stateMachineModel = new DefaultStateMachineModel<>(configurationData, statesData, transitionsData);
return stateMachineModel;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
@Override
protected AnnotationConfigApplicationContext buildContext() {
return new AnnotationConfigApplicationContext();
}
}