Add builder pattern for state machine instantiation

- Relates to #45
- Modify current annotation builder model to work via
  manual StateMachineBuilder class.
- Add ConfigurationConfigurer to configure bean factory,
  task executor and task scheduler via builder.
This commit is contained in:
Janne Valkealahti
2015-06-13 22:17:06 +01:00
parent c03b998d1e
commit 608183f2a3
15 changed files with 770 additions and 21 deletions

View File

@@ -16,6 +16,8 @@
package org.springframework.statemachine.config;
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
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.StateMachineStateBuilder;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
@@ -36,17 +38,23 @@ public abstract class AbstractStateMachineConfigurerAdapter<S, E> implements Sta
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(StateMachineTransitionBuilder.class, getStateMachineTransitionBuilder());
config.setSharedObject(StateMachineStateBuilder.class, getStateMachineStateBuilder());
config.setSharedObject(StateMachineConfigurationBuilder.class, getStateMachineConfigurationBuilder());
}
@Override
public void configure(StateMachineConfigBuilder<S, E> config) throws Exception {
}
@Override
public void configure(StateMachineConfigurationConfigurer<S, E> config) throws Exception {
}
@Override
public void configure(StateMachineStateConfigurer<S, E> states) throws Exception {
}
@@ -78,4 +86,13 @@ public abstract class AbstractStateMachineConfigurerAdapter<S, E> implements Sta
return stateBuilder;
}
protected final StateMachineConfigurationBuilder<S, E> getStateMachineConfigurationBuilder() throws Exception {
if (configurationBuilder != null) {
return configurationBuilder;
}
configurationBuilder = new StateMachineConfigurationBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
configure(configurationBuilder);
return configurationBuilder;
}
}

View File

@@ -25,12 +25,15 @@ import java.util.Map.Entry;
import java.util.Stack;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.task.TaskExecutor;
import org.springframework.messaging.Message;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.access.StateMachineAccess;
import org.springframework.statemachine.access.StateMachineFunction;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfig;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.builders.StateMachineTransitions.ChoiceData;
@@ -77,16 +80,20 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
private final StateMachineStates<S, E> stateMachineStates;
private final StateMachineConfigurationConfig<S,E> stateMachineConfigurationConfig;
private Boolean contextEvents;
/**
* Instantiates a new enum state machine factory.
*
* @param stateMachineConfigurationConfig the state machine generic config
* @param stateMachineTransitions the state machine transitions
* @param stateMachineStates the state machine states
*/
public AbstractStateMachineFactory(StateMachineTransitions<S, E> stateMachineTransitions,
StateMachineStates<S, E> stateMachineStates) {
public AbstractStateMachineFactory(StateMachineConfigurationConfig<S, E> stateMachineConfigurationConfig,
StateMachineTransitions<S, E> stateMachineTransitions, StateMachineStates<S, E> stateMachineStates) {
this.stateMachineConfigurationConfig = stateMachineConfigurationConfig;
this.stateMachineTransitions = stateMachineTransitions;
this.stateMachineStates = stateMachineStates;
}
@@ -144,7 +151,8 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
if (initialCount > 1) {
for (Collection<StateData<S, E>> regionStateDatas : regionsStateDatas) {
machine = buildMachine(machineMap, stateMap, regionStateDatas, transitionsData, getBeanFactory(),
contextEvents, defaultExtendedState, stateMachineTransitions);
contextEvents, defaultExtendedState, stateMachineTransitions, getTaskExecutor(),
getTaskScheduler());
regionStack.push(new MachineStackItem<S, E>(machine));
}
@@ -163,12 +171,13 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
states.add(rstate);
Transition<S, E> initialTransition = new InitialTransition<S, E>(rstate);
StateMachine<S, E> m = buildStateMachineInternal(states, new ArrayList<Transition<S, E>>(), rstate,
initialTransition, null, defaultExtendedState, null, contextEvents, getBeanFactory());
initialTransition, null, defaultExtendedState, null, contextEvents, getBeanFactory(),
getTaskExecutor(), getTaskScheduler());
machine = m;
}
} else {
machine = buildMachine(machineMap, stateMap, stateDatas, transitionsData, getBeanFactory(),
contextEvents, defaultExtendedState, stateMachineTransitions);
contextEvents, defaultExtendedState, stateMachineTransitions, getTaskExecutor(), getTaskScheduler());
if (peek.isInitial() || (!peek.isInitial() && !machineMap.containsKey(peek.getParent()))) {
machineMap.put(peek.getParent(), machine);
}
@@ -281,7 +290,8 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
Map<Object, StateMachine<S, E>> machineMap, Map<S, State<S, E>> stateMap,
Collection<StateData<S, E>> stateDatas, Collection<TransitionData<S, E>> transitionsData,
BeanFactory beanFactory, Boolean contextEvents, DefaultExtendedState defaultExtendedState,
StateMachineTransitions<S, E> stateMachineTransitions) {
StateMachineTransitions<S, E> stateMachineTransitions, TaskExecutor taskExecutor,
TaskScheduler taskScheduler) {
State<S, E> state = null;
State<S, E> initialState = null;
PseudoState<S, E> historyState = null;
@@ -451,13 +461,15 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
Transition<S, E> initialTransition = new InitialTransition<S, E>(initialState, initialAction);
StateMachine<S, E> machine = buildStateMachineInternal(states, transitions, initialState, initialTransition, null,
defaultExtendedState, historyState, contextEvents, beanFactory);
defaultExtendedState, historyState, contextEvents, beanFactory, taskExecutor, taskScheduler);
return machine;
}
protected abstract StateMachine<S, E> buildStateMachineInternal(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
State<S, E> initialState, Transition<S, E> initialTransition,
Message<E> initialEvent, ExtendedState extendedState, PseudoState<S, E> historyState, Boolean contextEventsEnabled, BeanFactory beanFactory);
protected abstract StateMachine<S, E> buildStateMachineInternal(Collection<State<S, E>> states,
Collection<Transition<S, E>> transitions, State<S, E> initialState, Transition<S, E> initialTransition,
Message<E> initialEvent, ExtendedState extendedState, PseudoState<S, E> historyState,
Boolean contextEventsEnabled, BeanFactory beanFactory, TaskExecutor taskExecutor,
TaskScheduler taskScheduler);
protected abstract State<S, E> buildStateInternal(S id, Collection<E> deferred, Collection<? extends Action<S, E>> entryActions, Collection<? extends Action<S, E>> exitActions,
PseudoState<S, E> pseudoState);

View File

@@ -18,11 +18,14 @@ package org.springframework.statemachine.config;
import java.util.Collection;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.task.TaskExecutor;
import org.springframework.messaging.Message;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.statemachine.ObjectStateMachine;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfig;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.state.ObjectState;
@@ -41,15 +44,17 @@ import org.springframework.statemachine.transition.Transition;
*/
public class ObjectStateMachineFactory<S, E> extends AbstractStateMachineFactory<S, E> {
public ObjectStateMachineFactory(StateMachineTransitions<S, E> stateMachineTransitions,
public ObjectStateMachineFactory(StateMachineConfigurationConfig<S,E> stateMachineConfigurationConfig, StateMachineTransitions<S, E> stateMachineTransitions,
StateMachineStates<S, E> stateMachineStates) {
super(stateMachineTransitions, stateMachineStates);
super(stateMachineConfigurationConfig, stateMachineTransitions, stateMachineStates);
}
@Override
protected StateMachine<S, E> buildStateMachineInternal(Collection<State<S, E>> states,
Collection<Transition<S, E>> transitions, State<S, E> initialState, Transition<S, E> initialTransition,
Message<E> initialEvent, ExtendedState extendedState, PseudoState<S, E> historyState, Boolean contextEventsEnabled, BeanFactory beanFactory) {
Message<E> initialEvent, ExtendedState extendedState, PseudoState<S, E> historyState,
Boolean contextEventsEnabled, BeanFactory beanFactory, TaskExecutor taskExecutor,
TaskScheduler taskScheduler) {
ObjectStateMachine<S, E> machine = new ObjectStateMachine<S, E>(states, transitions, initialState, initialTransition, initialEvent,
extendedState);
machine.setHistoryState(historyState);
@@ -59,6 +64,12 @@ public class ObjectStateMachineFactory<S, E> extends AbstractStateMachineFactory
if (beanFactory != null) {
machine.setBeanFactory(beanFactory);
}
if (taskExecutor != null) {
machine.setTaskExecutor(taskExecutor);
}
if (taskScheduler != null) {
machine.setTaskScheduler(taskScheduler);;
}
machine.afterPropertiesSet();
return machine;
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright 2015 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;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.StateMachineException;
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
import org.springframework.statemachine.config.builders.StateMachineConfigurationBuilder;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfig;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateBuilder;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
/**
* {@code StateMachineBuilder} provides a builder pattern for
* {@link StateMachine} using a similar concepts found from a
* normal annotation based configuration.
*
* @author Janne Valkealahti
*
*/
public class StateMachineBuilder {
/**
* Gets a builder for a {@link StateMachine}.
*
* @param <S> the type of state
* @param <E> the type of event
* @return the builder
*/
public static <S, E> Builder<S, E> builder() {
return new Builder<S, E>();
}
/**
* {@code Builder} implementation handling logic of building
* a {@link StateMachine} manually.
*
* @param <S> the type of state
* @param <E> the type of event
*/
public static class Builder<S, E> {
private StateMachineConfigBuilder<S, E> builder;
private BuilderStateMachineConfigurerAdapter<S, E> adapter;
/**
* Instantiates a new builder.
*/
public Builder() {
adapter = new BuilderStateMachineConfigurerAdapter<S, E>();
builder = new StateMachineConfigBuilder<S, E>();
}
/**
* Configure configuration.
*
* @return the state machine configuration configurer
*/
public StateMachineConfigurationConfigurer<S, E> configureConfiguration() {
return adapter.configurationBuilder;
}
/**
* Configure states.
*
* @return the state machine state configurer
*/
public StateMachineStateConfigurer<S, E> configureStates() {
return adapter.stateBuilder;
}
/**
* Configure transitions.
*
* @return the state machine transition configurer
*/
public StateMachineTransitionConfigurer<S, E> configureTransitions() {
return adapter.transitionBuilder;
}
/**
* Builds a {@link StateMachine}.
*
* @return the state machine
*/
public StateMachine<S, E> build() {
try {
builder.apply(adapter);
StateMachineConfig<S, E> stateMachineConfig = builder.getOrBuild();
StateMachineTransitions<S, E> stateMachineTransitions = stateMachineConfig.getTransitions();
StateMachineStates<S, E> stateMachineStates = stateMachineConfig.getStates();
StateMachineConfigurationConfig<S, E> stateMachineConfigurationConfig = stateMachineConfig.getStateMachineConfigurationConfig();
ObjectStateMachineFactory<S, E> stateMachineFactory = new ObjectStateMachineFactory<S, E>(
stateMachineConfigurationConfig, stateMachineTransitions, stateMachineStates);
if (stateMachineConfigurationConfig.getBeanFactory() != null) {
stateMachineFactory.setBeanFactory(stateMachineConfigurationConfig.getBeanFactory());
}
if (stateMachineConfigurationConfig.getTaskExecutor() != null) {
stateMachineFactory.setTaskExecutor(stateMachineConfigurationConfig.getTaskExecutor());
}
if (stateMachineConfigurationConfig.getTaskScheculer() != null) {
stateMachineFactory.setTaskScheduler(stateMachineConfigurationConfig.getTaskScheculer());
}
return stateMachineFactory.getStateMachine();
} catch (Exception e) {
throw new StateMachineException("Error building state machine", e);
}
}
}
private static class BuilderStateMachineConfigurerAdapter<S extends Object, E extends Object>
implements StateMachineConfigurer<S, E> {
private StateMachineTransitionBuilder<S, E> transitionBuilder;
private StateMachineStateBuilder<S, E> stateBuilder;
private StateMachineConfigurationBuilder<S, E> configurationBuilder;
BuilderStateMachineConfigurerAdapter() {
try {
getStateMachineTransitionBuilder();
getStateMachineStateBuilder();
getStateMachineConfigurationBuilder();
} catch (Exception e) {
throw new StateMachineException("Error instantiating builder adapter", e);
}
}
@Override
public void init(StateMachineConfigBuilder<S, E> config) throws Exception {
config.setSharedObject(StateMachineTransitionBuilder.class, getStateMachineTransitionBuilder());
config.setSharedObject(StateMachineStateBuilder.class, getStateMachineStateBuilder());
config.setSharedObject(StateMachineConfigurationBuilder.class, getStateMachineConfigurationBuilder());
}
@Override
public void configure(StateMachineConfigBuilder<S, E> builder) throws Exception {
}
@Override
public boolean isAssignable(AnnotationBuilder<StateMachineConfig<S, E>> builder) {
return false;
}
@Override
public void configure(StateMachineConfigurationConfigurer<S, E> config) throws Exception {
}
@Override
public void configure(StateMachineStateConfigurer<S, E> states) throws Exception {
}
@Override
public void configure(StateMachineTransitionConfigurer<S, E> transitions) throws Exception {
}
protected final StateMachineTransitionBuilder<S, E> getStateMachineTransitionBuilder() throws Exception {
if (transitionBuilder != null) {
return transitionBuilder;
}
transitionBuilder = new StateMachineTransitionBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
return transitionBuilder;
}
protected final StateMachineStateBuilder<S, E> getStateMachineStateBuilder() throws Exception {
if (stateBuilder != null) {
return stateBuilder;
}
stateBuilder = new StateMachineStateBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
return stateBuilder;
}
protected final StateMachineConfigurationBuilder<S, E> getStateMachineConfigurationBuilder() throws Exception {
if (configurationBuilder != null) {
return configurationBuilder;
}
configurationBuilder = new StateMachineConfigurationBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
return configurationBuilder;
}
}
}

View File

@@ -15,20 +15,28 @@
*/
package org.springframework.statemachine.config;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfig;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
public class StateMachineConfig<S, E> {
public final StateMachineConfigurationConfig<S, E> stateMachineConfigurationConfig;
public final StateMachineTransitions<S, E> transitions;
public final StateMachineStates<S, E> states;
public StateMachineConfig(StateMachineTransitions<S, E> transitions, StateMachineStates<S, E> states) {
public StateMachineConfig(StateMachineConfigurationConfig<S, E> stateMachineConfigurationConfig, StateMachineTransitions<S, E> transitions, StateMachineStates<S, E> states) {
this.stateMachineConfigurationConfig = stateMachineConfigurationConfig;
this.transitions = transitions;
this.states = states;
}
public StateMachineConfigurationConfig<S, E> getStateMachineConfigurationConfig() {
return stateMachineConfigurationConfig;
}
public StateMachineTransitions<S, E> getTransitions() {
return transitions;
}
@@ -36,4 +44,5 @@ public class StateMachineConfig<S, E> {
public StateMachineStates<S, E> getStates() {
return states;
}
}

View File

@@ -27,9 +27,11 @@ public class StateMachineConfigBuilder<S, E>
protected StateMachineConfig<S, E> performBuild() throws Exception {
StateMachineTransitionBuilder<?, ?> sharedObject = getSharedObject(StateMachineTransitionBuilder.class);
StateMachineStateBuilder<?, ?> sharedObject2 = getSharedObject(StateMachineStateBuilder.class);
StateMachineStates<S, E> states = (StateMachineStates<S, E>) sharedObject2.build();
StateMachineConfigurationBuilder<?, ?> sharedObject3 = getSharedObject(StateMachineConfigurationBuilder.class);
StateMachineTransitions<S, E> transitions = (StateMachineTransitions<S, E>) sharedObject.build();
StateMachineConfig<S, E> bean = new StateMachineConfig<S, E>(transitions, states);
StateMachineStates<S, E> states = (StateMachineStates<S, E>) sharedObject2.build();
StateMachineConfigurationConfig<S, E> config = (StateMachineConfigurationConfig<S, E>) sharedObject3.build();
StateMachineConfig<S, E> bean = new StateMachineConfig<S, E>(config, transitions, states);
return bean;
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2015 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.beans.factory.BeanFactory;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.TaskScheduler;
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.ConfigurationConfigurer;
import org.springframework.statemachine.config.configurers.DefaultConfigurationConfigurer;
/**
* {@link AnnotationBuilder} for {@link StateMachineStates}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class StateMachineConfigurationBuilder<S, E>
extends AbstractConfiguredAnnotationBuilder<StateMachineConfigurationConfig<S, E>, StateMachineConfigurationConfigurer<S, E>, StateMachineConfigurationBuilder<S, E>>
implements StateMachineConfigurationConfigurer<S, E> {
private BeanFactory beanFactory;
private TaskExecutor taskExecutor;
private TaskScheduler taskScheculer;
public StateMachineConfigurationBuilder() {
super();
}
public StateMachineConfigurationBuilder(ObjectPostProcessor<Object> objectPostProcessor,
boolean allowConfigurersOfSameType) {
super(objectPostProcessor, allowConfigurersOfSameType);
}
public StateMachineConfigurationBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor);
}
@Override
public ConfigurationConfigurer<S, E> withConfiguration() throws Exception {
return apply(new DefaultConfigurationConfigurer<S, E>());
}
@Override
protected StateMachineConfigurationConfig<S, E> performBuild() throws Exception {
return new StateMachineConfigurationConfig<>(beanFactory, taskExecutor, taskScheculer);
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void setTaskExecutor(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void setTaskScheculer(TaskScheduler taskScheculer) {
this.taskScheculer = taskScheculer;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2015 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.beans.factory.BeanFactory;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.TaskScheduler;
public class StateMachineConfigurationConfig<S, E> {
private final BeanFactory beanFactory;
private final TaskExecutor taskExecutor;
private final TaskScheduler taskScheculer;
public StateMachineConfigurationConfig(BeanFactory beanFactory, TaskExecutor taskExecutor,
TaskScheduler taskScheculer) {
this.beanFactory = beanFactory;
this.taskExecutor = taskExecutor;
this.taskScheculer = taskScheculer;
}
public BeanFactory getBeanFactory() {
return beanFactory;
}
public TaskExecutor getTaskExecutor() {
return taskExecutor;
}
public TaskScheduler getTaskScheculer() {
return taskScheculer;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2015 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.ConfigurationConfigurer;
/**
* Configurer interface exposing generic config.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface StateMachineConfigurationConfigurer<S, E> {
/**
* Gets a configurer for generic config.
*
* @return {@link ConfigurationConfigurer} for chaining
* @throws Exception if configuration error happens
*/
ConfigurationConfigurer<S, E> withConfiguration() throws Exception;
}

View File

@@ -20,7 +20,7 @@ import org.springframework.statemachine.config.common.annotation.AnnotationConfi
/**
* {@link AnnotationConfigurer} exposing configurers for states and transitions.
*
*
* @author Janne Valkealahti
*
* @param <S> the type of state
@@ -29,9 +29,17 @@ import org.springframework.statemachine.config.common.annotation.AnnotationConfi
public interface StateMachineConfigurer<S, E> extends
AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>> {
/**
* Callback for {@link StateMachineConfigurationConfigurer}.
*
* @param config the {@link StateMachineConfigurationConfigurer}
* @throws Exception if configuration error happens
*/
void configure(StateMachineConfigurationConfigurer<S, E> config) throws Exception;
/**
* Callback for {@link StateMachineStateConfigurer}.
*
*
* @param states the {@link StateMachineStateConfigurer}
* @throws Exception if configuration error happens
*/
@@ -39,7 +47,7 @@ public interface StateMachineConfigurer<S, E> extends
/**
* Callback for {@link StateMachineTransitionConfigurer}.
*
*
* @param transitions the {@link StateMachineTransitionConfigurer}
* @throws Exception if configuration error happens
*/

View File

@@ -29,6 +29,7 @@ import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.ObjectStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfig;
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfig;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.common.annotation.AbstractImportingAnnotationConfiguration;
@@ -88,8 +89,9 @@ public class StateMachineConfiguration<S extends Enum<S>, E extends Enum<E>> ext
StateMachineConfig<S, E> stateMachineConfig = getBuilder().getOrBuild();
StateMachineTransitions<S, E> stateMachineTransitions = stateMachineConfig.getTransitions();
StateMachineStates<S, E> stateMachineStates = stateMachineConfig.getStates();
StateMachineConfigurationConfig<S, E> stateMachineConfigurationConfig = stateMachineConfig.getStateMachineConfigurationConfig();
ObjectStateMachineFactory<S, E> stateMachineFactory = new ObjectStateMachineFactory<S, E>(
stateMachineTransitions, stateMachineStates);
stateMachineConfigurationConfig, stateMachineTransitions, stateMachineStates);
stateMachineFactory.setBeanFactory(getBeanFactory());
stateMachineFactory.setContextEventsEnabled(contextEvents);
setObject(stateMachineFactory.getStateMachine());

View File

@@ -35,6 +35,7 @@ import org.springframework.statemachine.config.ObjectStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfig;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfig;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.common.annotation.AbstractImportingAnnotationConfiguration;
@@ -109,7 +110,8 @@ public class StateMachineFactoryConfiguration<S extends Enum<S>, E extends Enum<
StateMachineConfig<S, E> stateMachineConfig = builder.getOrBuild();
StateMachineTransitions<S, E> stateMachineTransitions = stateMachineConfig.getTransitions();
StateMachineStates<S, E> stateMachineStates = stateMachineConfig.getStates();
ObjectStateMachineFactory<S,E> enumStateMachineFactory = new ObjectStateMachineFactory<S, E>(stateMachineTransitions, stateMachineStates);
StateMachineConfigurationConfig<S, E> stateMachineConfigurationConfig = stateMachineConfig.getStateMachineConfigurationConfig();
ObjectStateMachineFactory<S,E> enumStateMachineFactory = new ObjectStateMachineFactory<S, E>(stateMachineConfigurationConfig, stateMachineTransitions, stateMachineStates);
enumStateMachineFactory.setBeanFactory(beanFactory);
enumStateMachineFactory.setContextEventsEnabled(contextEvents);
this.stateMachineFactory = enumStateMachineFactory;

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2015 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.beans.factory.BeanFactory;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
/**
* Base {@code ConfigConfigurer} interface for configuring generic config.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface ConfigurationConfigurer<S, E> extends
AnnotationConfigurerBuilder<StateMachineConfigurationConfigurer<S, E>> {
/**
* Specify a {@link BeanFactory}.
*
* @param beanFactory the bean factory
* @return configurer for chaining
*/
ConfigurationConfigurer<S, E> beanFactory(BeanFactory beanFactory);
/**
* Specify a {@link TaskExecutor}.
*
* @param beanFactory the bean factory
* @return configurer for chaining
*/
ConfigurationConfigurer<S, E> taskExecutor(TaskExecutor taskExecutor);
/**
* Specify a {@link TaskScheduler}.
*
* @param beanFactory the bean factory
* @return configurer for chaining
*/
ConfigurationConfigurer<S, E> taskScheduler(TaskScheduler taskScheduler);
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2015 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.beans.factory.BeanFactory;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.statemachine.config.builders.StateMachineConfigurationBuilder;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfig;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
/**
* Default implementation of a {@link ConfigurationConfigurer}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class DefaultConfigurationConfigurer<S, E>
extends AnnotationConfigurerAdapter<StateMachineConfigurationConfig<S, E>, StateMachineConfigurationConfigurer<S, E>, StateMachineConfigurationBuilder<S, E>>
implements ConfigurationConfigurer<S, E> {
private BeanFactory beanFactory;
private TaskExecutor taskExecutor;
private TaskScheduler taskScheculer;
@Override
public void configure(StateMachineConfigurationBuilder<S, E> builder) throws Exception {
builder.setBeanFactory(beanFactory);
builder.setTaskExecutor(taskExecutor);
builder.setTaskScheculer(taskScheculer);
}
@Override
public ConfigurationConfigurer<S, E> beanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
return this;
}
@Override
public ConfigurationConfigurer<S, E> taskExecutor(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
return this;
}
@Override
public ConfigurationConfigurer<S, E> taskScheduler(TaskScheduler taskScheduler) {
this.taskScheculer = taskScheduler;
return this;
}
}

View File

@@ -0,0 +1,190 @@
/*
* Copyright 2015 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;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineBuilder.Builder;
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfig;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.transition.Transition;
public class ManualBuilderTests {
@Test
public void testManualBuildConcept() throws Exception {
StateMachineConfigBuilder<String, String> builder = new StateMachineConfigBuilder<String, String>();
Config config = new Config();
builder.apply(config);
StateMachineConfig<String, String> stateMachineConfig = builder.getOrBuild();
StateMachineTransitions<String, String> stateMachineTransitions = stateMachineConfig.getTransitions();
StateMachineStates<String, String> stateMachineStates = stateMachineConfig.getStates();
StateMachineConfigurationConfig<String, String> stateMachineConfigurationConfig = stateMachineConfig.getStateMachineConfigurationConfig();
ObjectStateMachineFactory<String, String> stateMachineFactory = new ObjectStateMachineFactory<String, String>(
stateMachineConfigurationConfig, stateMachineTransitions, stateMachineStates);
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
beanFactory.addBean("taskExecutor", new SyncTaskExecutor());
beanFactory.addBean("taskScheduler", new ConcurrentTaskScheduler());
stateMachineFactory.setBeanFactory(beanFactory);
TestListener listener = new TestListener();
StateMachine<String,String> stateMachine = stateMachineFactory.getStateMachine();
stateMachine.addStateListener(listener);
stateMachine.start();
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedCount, is(1));
assertThat(stateMachine, notNullValue());
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1"));
}
@Test
public void testManualBuildTaskViaBeanFactory() throws Exception {
Builder<String, String> builder = StateMachineBuilder.builder();
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
beanFactory.addBean("taskExecutor", new SyncTaskExecutor());
beanFactory.addBean("taskScheduler", new ConcurrentTaskScheduler());
builder.configureConfiguration()
.withConfiguration()
.beanFactory(beanFactory);
builder.configureStates()
.withStates()
.initial("S1").state("S2");
builder.configureTransitions()
.withExternal()
.source("S1").target("S2").event("E1")
.and()
.withExternal()
.source("S2").target("S1").event("E2");
StateMachine<String, String> stateMachine = builder.build();
assertThat(stateMachine, notNullValue());
TestListener listener = new TestListener();
stateMachine.addStateListener(listener);
stateMachine.start();
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedCount, is(1));
assertThat(stateMachine, notNullValue());
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1"));
}
@Test
public void testManualBuildExplicitTaskExecutorAndScheduler() throws Exception {
Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.taskExecutor(new SyncTaskExecutor())
.taskScheduler(new ConcurrentTaskScheduler());
builder.configureStates()
.withStates()
.initial("S1").state("S2");
builder.configureTransitions()
.withExternal()
.source("S1").target("S2").event("E1")
.and()
.withExternal()
.source("S2").target("S1").event("E2");
StateMachine<String, String> stateMachine = builder.build();
assertThat(stateMachine, notNullValue());
TestListener listener = new TestListener();
stateMachine.addStateListener(listener);
stateMachine.start();
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedCount, is(1));
assertThat(stateMachine, notNullValue());
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1"));
}
static class Config extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
states
.withStates()
.initial("S1").state("S2");
}
@Override
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
transitions
.withExternal()
.source("S1").target("S2").event("E1")
.and()
.withExternal()
.source("S2").target("S1").event("E2");
}
}
private static class TestListener extends StateMachineListenerAdapter<String, String> {
volatile CountDownLatch stateChangedLatch = new CountDownLatch(1);
volatile CountDownLatch transitionLatch = new CountDownLatch(0);
volatile int stateChangedCount = 0;
@Override
public void stateChanged(State<String, String> from, State<String, String> to) {
stateChangedCount++;
stateChangedLatch.countDown();
}
@Override
public void transition(Transition<String, String> transition) {
transitionLatch.countDown();
}
public void reset(int c1) {
reset(c1, 0);
}
public void reset(int c1, int c2) {
stateChangedLatch = new CountDownLatch(c1);
transitionLatch = new CountDownLatch(c2);
stateChangedCount = 0;
}
}
}