Add reactive state actions
- Add stateDoFunction, stateEntryFunction and stateExitFunction methods taking Function with a state to register reactive actions. - Don't yet to a full take on all similar action methods as we need to think about how these should be named and what old methods to depecate. - Relates #743
This commit is contained in:
@@ -162,7 +162,11 @@ public class DefaultStateConfigurer<S, E>
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> state(S state, Collection<? extends Action<S, E>> stateActions) {
|
||||
addIncomplete(null, state, null, null, null, stateActions);
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> rStateActions = new ArrayList<>();
|
||||
if (stateActions != null) {
|
||||
rStateActions.addAll(stateActions.stream().map(a -> Actions.from(a)).collect(Collectors.toList()));
|
||||
}
|
||||
addIncomplete(null, state, null, null, null, rStateActions);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -181,6 +185,30 @@ public class DefaultStateConfigurer<S, E>
|
||||
return stateDo(state, action, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> stateDoFunction(S state, Function<StateContext<S, E>, Mono<Void>> stateAction) {
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> stateActions = new ArrayList<>();
|
||||
stateActions.add(stateAction);
|
||||
addIncomplete(null, state, null, null, null, stateActions);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> stateEntryFunction(S state, Function<StateContext<S, E>, Mono<Void>> action) {
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> entryActions = new ArrayList<>();
|
||||
entryActions.add(action);
|
||||
addIncomplete(null, state, null, entryActions, null, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> stateExitFunction(S state, Function<StateContext<S, E>, Mono<Void>> action) {
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> exitActions = new ArrayList<>();
|
||||
exitActions.add(action);
|
||||
addIncomplete(null, state, null, null, exitActions, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> stateDo(S state, Action<S, E> action, Action<S, E> error) {
|
||||
Collection<Action<S, E>> stateActions = null;
|
||||
@@ -194,7 +222,17 @@ public class DefaultStateConfigurer<S, E>
|
||||
@Override
|
||||
public StateConfigurer<S, E> state(S state, Collection<? extends Action<S, E>> entryActions,
|
||||
Collection<? extends Action<S, E>> exitActions) {
|
||||
addIncomplete(null, state, null, entryActions, exitActions, null);
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> rEntryActions = null;
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> rExitActions = null;
|
||||
if (entryActions != null) {
|
||||
rEntryActions = new ArrayList<>();
|
||||
rEntryActions.addAll(entryActions.stream().map(a -> Actions.from(a)).collect(Collectors.toList()));
|
||||
}
|
||||
if (exitActions != null) {
|
||||
rExitActions = new ArrayList<>();
|
||||
rExitActions.addAll(exitActions.stream().map(a -> Actions.from(a)).collect(Collectors.toList()));
|
||||
}
|
||||
addIncomplete(null, state, null, rEntryActions, rExitActions, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -313,26 +351,12 @@ public class DefaultStateConfigurer<S, E>
|
||||
}
|
||||
|
||||
private void addIncomplete(Object parent, S state, Collection<E> deferred,
|
||||
Collection<? extends Action<S, E>> entryActions, Collection<? extends Action<S, E>> exitActions,
|
||||
Collection<? extends Action<S, E>> stateActions) {
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> rEntryActions = null;
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> rExitActions = null;
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> rStateActions = null;
|
||||
if (entryActions != null) {
|
||||
rEntryActions = new ArrayList<>();
|
||||
rEntryActions.addAll(entryActions.stream().map(a -> Actions.from(a)).collect(Collectors.toList()));
|
||||
}
|
||||
if (exitActions != null) {
|
||||
rExitActions = new ArrayList<>();
|
||||
rExitActions.addAll(exitActions.stream().map(a -> Actions.from(a)).collect(Collectors.toList()));
|
||||
}
|
||||
if (stateActions != null) {
|
||||
rStateActions = new ArrayList<>();
|
||||
rStateActions.addAll(stateActions.stream().map(a -> Actions.from(a)).collect(Collectors.toList()));
|
||||
}
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> entryActions,
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> exitActions,
|
||||
Collection<Function<StateContext<S, E>, Mono<Void>>> stateActions) {
|
||||
StateData<S, E> stateData = incomplete.get(state);
|
||||
if (stateData == null) {
|
||||
stateData = new StateData<S, E>(parent, region, state, deferred, rEntryActions, rExitActions);
|
||||
stateData = new StateData<S, E>(parent, region, state, deferred, entryActions, exitActions);
|
||||
incomplete.put(state, stateData);
|
||||
}
|
||||
if (stateData.getParent() == null) {
|
||||
@@ -345,13 +369,13 @@ public class DefaultStateConfigurer<S, E>
|
||||
stateData.setDeferred(deferred);
|
||||
}
|
||||
if (stateData.getEntryActions() == null) {
|
||||
stateData.setEntryActions(rEntryActions);
|
||||
stateData.setEntryActions(entryActions);
|
||||
}
|
||||
if (stateData.getExitActions() == null) {
|
||||
stateData.setExitActions(rExitActions);
|
||||
stateData.setExitActions(exitActions);
|
||||
}
|
||||
if (stateData.getStateActions() == null) {
|
||||
stateData.setStateActions(rStateActions);
|
||||
stateData.setStateActions(stateActions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ package org.springframework.statemachine.config.configurers;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
@@ -25,6 +27,8 @@ import org.springframework.statemachine.config.builders.StateMachineStateConfigu
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Base {@code StateConfigurer} interface for configuring {@link State}s.
|
||||
*
|
||||
@@ -139,6 +143,33 @@ public interface StateConfigurer<S, E> extends
|
||||
*/
|
||||
StateConfigurer<S, E> stateDo(S state, Action<S, E> action, Action<S, E> error);
|
||||
|
||||
/**
|
||||
* Specify a state {@code S} with state behaviour {@link Function}.
|
||||
*
|
||||
* @param state the state
|
||||
* @param action the state action
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
StateConfigurer<S, E> stateDoFunction(S state, Function<StateContext<S, E>, Mono<Void>> action);
|
||||
|
||||
/**
|
||||
* Specify a state {@code S} with state entry {@link Function}.
|
||||
*
|
||||
* @param state the state
|
||||
* @param action the state action
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
StateConfigurer<S, E> stateEntryFunction(S state, Function<StateContext<S, E>, Mono<Void>> action);
|
||||
|
||||
/**
|
||||
* Specify a state {@code S} with state exit {@link Function}.
|
||||
*
|
||||
* @param state the state
|
||||
* @param action the state action
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
StateConfigurer<S, E> stateExitFunction(S state, Function<StateContext<S, E>, Mono<Void>> action);
|
||||
|
||||
/**
|
||||
* Specify a state {@code S} with entry and exit {@link Action}s.
|
||||
*
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package org.springframework.statemachine.action;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
@@ -45,10 +46,9 @@ import reactor.core.publisher.Mono;
|
||||
*/
|
||||
public class ReactiveActionTests extends AbstractStateMachineTests {
|
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Test
|
||||
public void testSimpleReactiveAction() {
|
||||
public void testSimpleReactiveActions() throws Exception {
|
||||
context.register(Config1.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@@ -57,8 +57,19 @@ public class ReactiveActionTests extends AbstractStateMachineTests {
|
||||
machine.start();
|
||||
|
||||
TestCountAction testAction1 = context.getBean("testAction1", TestCountAction.class);
|
||||
TestCountAction testAction2 = context.getBean("testAction2", TestCountAction.class);
|
||||
TestCountAction testAction3 = context.getBean("testAction3", TestCountAction.class);
|
||||
TestCountAction testAction4 = context.getBean("testAction4", TestCountAction.class);
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E2).build());
|
||||
assertThat(testAction1.latch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(testAction2.latch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(testAction3.latch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(testAction4.latch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(testAction1.count, is(1));
|
||||
assertThat(testAction2.count, is(1));
|
||||
assertThat(testAction3.count, is(1));
|
||||
assertThat(testAction4.count, is(1));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -70,7 +81,9 @@ public class ReactiveActionTests extends AbstractStateMachineTests {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.state(TestStates.S2);
|
||||
.stateExitFunction(TestStates.S2, testAction2())
|
||||
.stateDoFunction(TestStates.S3, testAction3())
|
||||
.stateEntryFunction(TestStates.S3, testAction4());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,13 +93,33 @@ public class ReactiveActionTests extends AbstractStateMachineTests {
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1)
|
||||
.actionFunction(testAction1());
|
||||
.actionFunction(testAction1())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S2)
|
||||
.target(TestStates.S3)
|
||||
.event(TestEvents.E2);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestCountAction testAction1() {
|
||||
return new TestCountAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestCountAction testAction2() {
|
||||
return new TestCountAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestCountAction testAction3() {
|
||||
return new TestCountAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestCountAction testAction4() {
|
||||
return new TestCountAction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,6 +34,8 @@ import org.springframework.statemachine.config.builders.StateMachineStateBuilder
|
||||
import org.springframework.statemachine.config.model.StateData;
|
||||
import org.springframework.statemachine.state.PseudoStateKind;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public class DefaultStateConfigurerTests {
|
||||
|
||||
@Test
|
||||
@@ -136,6 +138,26 @@ public class DefaultStateConfigurerTests {
|
||||
assertThat(builder.data.iterator().next().getEntryActions(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateActionFunctions() throws Exception {
|
||||
DefaultStateConfigurer<TestStates, TestEvents> configurer = new DefaultStateConfigurer<TestStates, TestEvents>();
|
||||
TestStateMachineStateBuilder builder = new TestStateMachineStateBuilder();
|
||||
configurer.stateDoFunction(TestStates.S2, context -> Mono.empty());
|
||||
configurer.stateEntryFunction(TestStates.S2, context -> Mono.empty());
|
||||
configurer.stateExitFunction(TestStates.S2, context -> Mono.empty());
|
||||
configurer.configure(builder);
|
||||
assertThat(builder.data, notNullValue());
|
||||
assertThat(builder.data.size(), is(1));
|
||||
|
||||
assertThat(builder.data.iterator().next().getState(), is(TestStates.S2));
|
||||
assertThat(builder.data.iterator().next().getExitActions(), notNullValue());
|
||||
assertThat(builder.data.iterator().next().getExitActions().size(), is(1));
|
||||
assertThat(builder.data.iterator().next().getStateActions(), notNullValue());
|
||||
assertThat(builder.data.iterator().next().getStateActions().size(), is(1));
|
||||
assertThat(builder.data.iterator().next().getEntryActions(), notNullValue());
|
||||
assertThat(builder.data.iterator().next().getEntryActions().size(), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndStateNoState() throws Exception {
|
||||
DefaultStateConfigurer<TestStates, TestEvents> configurer = new DefaultStateConfigurer<TestStates, TestEvents>();
|
||||
|
||||
Reference in New Issue
Block a user