/*
* 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.zookeeper;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
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.StateMachineBuilder;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.ensemble.DistributedStateMachine;
import org.springframework.statemachine.ensemble.StateMachineEnsemble;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.test.StateMachineTestPlan;
import org.springframework.statemachine.test.StateMachineTestPlanBuilder;
import org.springframework.statemachine.transition.Transition;
public class ZookeeperStateMachineTests extends AbstractZookeeperTests {
@Override
protected AnnotationConfigApplicationContext buildContext() {
return new AnnotationConfigApplicationContext();
}
@Test
@SuppressWarnings("unchecked")
public void testStateChangesManualSetup() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class, Config1.class, Config2.class);
context.refresh();
StateMachine<String, String> machine1 =
context.getBean("sm1", StateMachine.class);
StateMachine<String, String> machine2 =
context.getBean("sm2", StateMachine.class);
TestListener listener1 = new TestListener();
TestListener listener2 = new TestListener();
machine1.addStateListener(listener1);
machine2.addStateListener(listener2);
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
ZookeeperStateMachineEnsemble<String, String> ensemble1 =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
ZookeeperStateMachineEnsemble<String, String> ensemble2 =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
ensemble1.afterPropertiesSet();
ensemble2.afterPropertiesSet();
ensemble1.start();
ensemble2.start();
DistributedStateMachine<String, String> machine1s =
new DistributedStateMachine<String, String>(ensemble1, machine1);
DistributedStateMachine<String, String> machine2s =
new DistributedStateMachine<String, String>(ensemble2, machine2);
machine1s.afterPropertiesSet();
machine2s.afterPropertiesSet();
machine1s.start();
machine2s.start();
listener1.reset(1);
listener2.reset(1);
machine1s.sendEvent("E1");
assertThat(listener1.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener1.stateChangedCount, is(1));
assertThat(listener2.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener2.stateChangedCount, is(1));
assertThat(machine1.getState().getIds(), containsInAnyOrder("S1"));
assertThat(machine2.getState().getIds(), containsInAnyOrder("S1"));
listener1.reset(1);
listener2.reset(1);
machine1s.sendEvent("E2");
assertThat(listener1.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener1.stateChangedCount, is(1));
assertThat(listener2.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener2.stateChangedCount, is(1));
assertThat(machine1.getState().getIds(), containsInAnyOrder("S2"));
assertThat(machine2.getState().getIds(), containsInAnyOrder("S2"));
}
@Test
@SuppressWarnings("unchecked")
public void testStateChangesManualSetupSendDifferentMachines() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class, Config1.class, Config2.class);
context.refresh();
StateMachine<String, String> machine1 =
context.getBean("sm1", StateMachine.class);
StateMachine<String, String> machine2 =
context.getBean("sm2", StateMachine.class);
TestListener listener1 = new TestListener();
TestListener listener2 = new TestListener();
machine1.addStateListener(listener1);
machine2.addStateListener(listener2);
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
ZookeeperStateMachineEnsemble<String, String> ensemble1 =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
ZookeeperStateMachineEnsemble<String, String> ensemble2 =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
ensemble1.afterPropertiesSet();
ensemble2.afterPropertiesSet();
ensemble1.start();
ensemble2.start();
DistributedStateMachine<String, String> machine1s =
new DistributedStateMachine<String, String>(ensemble1, machine1);
DistributedStateMachine<String, String> machine2s =
new DistributedStateMachine<String, String>(ensemble2, machine2);
machine1s.afterPropertiesSet();
machine2s.afterPropertiesSet();
machine1s.start();
machine2s.start();
listener1.reset(1);
listener2.reset(1);
machine1s.sendEvent("E1");
assertThat(listener1.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener1.stateChangedCount, is(1));
assertThat(listener2.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener2.stateChangedCount, is(1));
assertThat(machine1.getState().getIds(), containsInAnyOrder("S1"));
assertThat(machine2.getState().getIds(), containsInAnyOrder("S1"));
listener1.reset(1);
listener2.reset(1);
machine2s.sendEvent("E2");
assertThat(listener1.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener1.stateChangedCount, is(1));
assertThat(listener2.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener2.stateChangedCount, is(1));
assertThat(machine1.getState().getIds(), containsInAnyOrder("S2"));
assertThat(machine2.getState().getIds(), containsInAnyOrder("S2"));
}
@Test
@SuppressWarnings("unchecked")
public void testLifecycle() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class, Config3.class, Config4.class);
context.refresh();
StateMachine<String, String> machine1 =
context.getBean("sm1", StateMachine.class);
StateMachine<String, String> machine2 =
context.getBean("sm2", StateMachine.class);
assertThat(((SmartLifecycle)machine1).isAutoStartup(), is(false));
assertThat(((SmartLifecycle)machine1).isRunning(), is(false));
assertThat(((SmartLifecycle)machine2).isAutoStartup(), is(false));
assertThat(((SmartLifecycle)machine2).isRunning(), is(false));
}
@Test
@SuppressWarnings("unchecked")
public void testStateChangesConfigSetup() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class, Config3.class, Config4.class);
context.refresh();
StateMachine<String, String> machine1 =
context.getBean("sm1", StateMachine.class);
StateMachine<String, String> machine2 =
context.getBean("sm2", StateMachine.class);
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(machine1)
.stateMachine(machine2)
.step().expectState("SI").and()
.step().sendEvent("E1").expectStateChanged(1).expectState("S1").and()
.step().sendEvent("E2").expectStateChanged(1).expectState("S2").and()
.build();
plan.test();
}
@Test
public void testVariousChangesInShowcase() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
StateMachine<String, String> machine1 =
buildTestStateMachine(curatorClient);
StateMachine<String, String> machine2 =
buildTestStateMachine(curatorClient);
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.stateMachine(machine2)
.step()
.expectStates("S0", "S1", "S11")
.expectVariable("foo")
.expectVariable("foo", 0)
.and()
.step()
.sendEvent("C", machine1)
.expectStateChanged(3)
.expectStates("S0", "S2", "S21", "S211")
.and()
.step()
.sendEvent("C", machine2)
.expectStateChanged(2)
.expectStates("S0", "S1", "S11")
.and()
.build();
plan.test();
}
@Test
public void testShouldHaveCasErrorDoesNotBreakMachines() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
StateMachine<String, String> machine1 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine2 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine3 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine4 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine5 =
buildTestStateMachine2(curatorClient);
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.stateMachine(machine2)
.stateMachine(machine3)
.stateMachine(machine4)
.stateMachine(machine5)
.step()
.expectStates("SI")
.and()
.step()
.sendEvent("E1", true)
.expectStateChanged(1)
.expectStates("S1")
.and()
.step()
.sendEvent("E2", true)
.expectStateChanged(1)
.expectStates("S2")
.and()
.build();
plan.test();
}
@Test
public void testParallelEvents() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
StateMachine<String, String> machine1 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine2 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine3 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine4 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine5 =
buildTestStateMachine2(curatorClient);
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.stateMachine(machine2)
.stateMachine(machine3)
.stateMachine(machine4)
.stateMachine(machine5)
.step()
.expectStates("SI")
.and()
.step()
.sendEvent("E1", true)
.expectStateChanged(1)
.expectStates("S1")
.and()
.step()
.sendEvent("E2", true, true)
.expectStateChanged(1)
.expectStates("S2")
.and()
.step()
.sendEvent("E3", true, true)
.expectStateChanged(1)
.expectStates("S1")
.and()
.step()
.sendEvent("E2", true, true)
.expectStateChanged(1)
.expectStates("S2")
.and()
.step()
.sendEvent("E3", true, true)
.expectStateChanged(1)
.expectStates("S1")
.and()
.step()
.sendEvent("E2", true, true)
.expectStateChanged(1)
.expectStates("S2")
.and()
.step()
.sendEvent("E3", true, true)
.expectStateChanged(1)
.expectStates("S1")
.and()
.build();
plan.test();
}
@Test
public void testExtendedStateVariables1() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
StateMachine<String, String> machine1 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine2 =
buildTestStateMachine2(curatorClient);
Message<String> message = MessageBuilder
.withPayload("EV")
.setHeader("testVariable", "x1")
.build();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.stateMachine(machine2)
.step()
.expectStates("SI")
.and()
.step()
.sendEvent(message, machine1)
.expectTransition(1)
.expectVariable("testVariable", "x1")
.and()
.build();
plan.test();
}
@Test
public void testExtendedStateVariables2() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
StateMachine<String, String> machine1 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine2 =
buildTestStateMachine2(curatorClient);
Message<String> message = MessageBuilder
.withPayload("EV")
.setHeader("testVariable", "x1")
.build();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.stateMachine(machine2)
.step()
.expectStates("SI")
.and()
.step()
.sendEvent("E1", machine1)
.expectStateChanged(1)
.expectStates("S1")
.and()
.step()
.sendEvent(message, machine1)
.expectTransition(1)
.expectExtendedStateChanged(1)
.expectVariable("testVariable", "x1")
.and()
.build();
plan.test();
}
@Test
@SuppressWarnings("unchecked")
public void testJoinLaterShouldSyncState() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class, Config1.class, Config2.class);
context.refresh();
StateMachine<String, String> machine1 =
context.getBean("sm1", StateMachine.class);
StateMachine<String, String> machine2 =
context.getBean("sm2", StateMachine.class);
TestListener listener1 = new TestListener();
TestListener listener2 = new TestListener();
machine1.addStateListener(listener1);
machine2.addStateListener(listener2);
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
ZookeeperStateMachineEnsemble<String, String> ensemble1 =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
ensemble1.afterPropertiesSet();
ensemble1.start();
DistributedStateMachine<String, String> machine1s =
new DistributedStateMachine<String, String>(ensemble1, machine1);
machine1s.afterPropertiesSet();
machine1s.start();
listener1.reset(1);
machine1s.sendEvent("E1");
assertThat(listener1.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener1.stateChangedCount, is(1));
assertThat(machine1.getState().getIds(), containsInAnyOrder("S1"));
ZookeeperStateMachineEnsemble<String, String> ensemble2 =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
ensemble2.afterPropertiesSet();
ensemble2.start();
DistributedStateMachine<String, String> machine2s =
new DistributedStateMachine<String, String>(ensemble2, machine2);
machine2s.afterPropertiesSet();
machine2s.start();
assertThat(machine2.getState().getIds(), containsInAnyOrder("S1"));
}
@Test
public void testConnectionLoss1() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
StateMachine<String, String> machine1 =
buildTestStateMachine(curatorClient);
StateMachineTestPlan<String, String> plan1 =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.step()
.expectStates("S0", "S1", "S11")
.and()
.step()
.sendEvent("C", machine1)
.expectStateChanged(3)
.expectStates("S0", "S2", "S21", "S211")
.and()
.build();
plan1.test();
Object ensemble = TestUtils.readField("ensemble", machine1);
try {
TestUtils.callMethod("handleZkDisconnect", ensemble);
} catch (Exception e) {
}
TestUtils.callMethod("handleZkConnect", ensemble);
StateMachineTestPlan<String, String> plan2 =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.step()
.expectStates("S0", "S2", "S21", "S211")
.and()
.step()
.sendEvent("C", machine1)
.expectStateChanged(2)
.expectStates("S0", "S1", "S11")
.and()
.build();
plan2.test();
}
@Test
public void testConnectionLoss2() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
StateMachine<String, String> machine1 =
buildTestStateMachine(curatorClient);
StateMachine<String, String> machine2 =
buildTestStateMachine(curatorClient);
StateMachine<String, String> machine3 =
buildTestStateMachine(curatorClient);
StateMachine<String, String> machine4 =
buildTestStateMachine(curatorClient);
StateMachine<String, String> machine5 =
buildTestStateMachine(curatorClient);
StateMachineTestPlan<String, String> plan1 =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.stateMachine(machine2)
.stateMachine(machine3)
.stateMachine(machine4)
.stateMachine(machine5)
.step()
.expectStates("S0", "S1", "S11")
.and()
.step()
.sendEvent("C", machine1)
.expectStateChanged(3)
.expectStates("S0", "S2", "S21", "S211")
.and()
.build();
plan1.test();
Object ensemble2 = TestUtils.readField("ensemble", machine2);
Object ensemble3 = TestUtils.readField("ensemble", machine3);
Object ensemble4 = TestUtils.readField("ensemble", machine4);
Object ensemble5 = TestUtils.readField("ensemble", machine5);
try {
TestUtils.callMethod("handleZkDisconnect", ensemble2);
TestUtils.callMethod("handleZkDisconnect", ensemble3);
TestUtils.callMethod("handleZkDisconnect", ensemble4);
TestUtils.callMethod("handleZkDisconnect", ensemble5);
} catch (Exception e) {
}
TestUtils.callMethod("handleZkConnect", ensemble2);
TestUtils.callMethod("handleZkConnect", ensemble3);
TestUtils.callMethod("handleZkConnect", ensemble4);
TestUtils.callMethod("handleZkConnect", ensemble5);
StateMachineTestPlan<String, String> plan2 =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.stateMachine(machine2)
.stateMachine(machine3)
.stateMachine(machine4)
.stateMachine(machine5)
.step()
.expectStates("S0", "S2", "S21", "S211")
.and()
.step()
.sendEvent("C", machine1)
.expectStateChanged(2)
.expectStates("S0", "S1", "S11")
.and()
.step()
.sendEvent("C", machine2)
.expectStateChanged(3)
.expectStates("S0", "S2", "S21", "S211")
.and()
.step()
.sendEvent("C", machine3)
.expectStateChanged(2)
.expectStates("S0", "S1", "S11")
.and()
.step()
.sendEvent("C", machine4)
.expectStateChanged(3)
.expectStates("S0", "S2", "S21", "S211")
.and()
.step()
.sendEvent("C", machine5)
.expectStateChanged(2)
.expectStates("S0", "S1", "S11")
.and()
.build();
plan2.test();
}
@Configuration
@EnableStateMachine(name = "sm1")
static class Config1 extends SharedConfig1 {
}
@Configuration
@EnableStateMachine(name = "sm2")
static class Config2 extends SharedConfig1 {
}
@Configuration
@EnableStateMachine(name = "sm1")
static class Config3 extends SharedConfig2 {
@Autowired
private CuratorFramework curatorClient;
@Override
@Bean(name = "listener1")
public TestListener stateMachineListener() {
return new TestListener();
}
@Override
@Bean(name = "ensemble1")
public StateMachineEnsemble<String, String> stateMachineEnsemble() throws Exception {
return new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
}
}
@Configuration
@EnableStateMachine(name = "sm2")
static class Config4 extends SharedConfig2 {
@Autowired
private CuratorFramework curatorClient;
@Override
@Bean(name = "listener2")
public TestListener stateMachineListener() {
return new TestListener();
}
@Override
@Bean(name = "ensemble2")
public StateMachineEnsemble<String, String> stateMachineEnsemble() throws Exception {
return new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
}
}
public abstract static class SharedConfig2 extends SharedConfig1 {
@Override
public void configure(StateMachineConfigurationConfigurer<String, String> config) throws Exception {
config
.withDistributed()
.ensemble(stateMachineEnsemble())
.and()
.withConfiguration()
.listener(stateMachineListener())
// TODO: false, really? testStateChangesConfigSetup() will fail if true
// maybe it's due to dist needs to be reseted!
// previously setting it true, didn't actually enable autostart.
.autoStartup(false);
}
public abstract StateMachineEnsemble<String, String> stateMachineEnsemble() throws Exception;
public abstract TestListener stateMachineListener();
}
static class SharedConfig1 extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
states
.withStates()
.initial("SI")
.state("S1")
.state("S2");
}
@Override
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
transitions
.withExternal()
.source("SI")
.target("S1")
.event("E1")
.and()
.withExternal()
.source("S1")
.target("S2")
.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;
volatile CountDownLatch stateMachineStartedLatch = new CountDownLatch(1);
@Override
public void stateMachineStarted(StateMachine<String, String> stateMachine) {
stateMachineStartedLatch.countDown();
}
@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;
}
}
private static StateMachineEnsemble<String, String> stateMachineEnsemble(CuratorFramework curatorClient) {
ZookeeperStateMachineEnsemble<String, String> ensemble = new ZookeeperStateMachineEnsemble<String, String>(
curatorClient, "/foo");
ensemble.afterPropertiesSet();
ensemble.start();
return ensemble;
}
private StateMachine<String, String> buildTestStateMachine(CuratorFramework curatorClient)
throws Exception {
StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.taskExecutor(new SyncTaskExecutor())
.autoStartup(true)
.and()
.withDistributed()
.ensemble(stateMachineEnsemble(curatorClient));
builder.configureStates()
.withStates()
.initial("S0", fooAction())
.state("S0")
.and()
.withStates()
.parent("S0")
.initial("S1")
.state("S1")
.and()
.withStates()
.parent("S1")
.initial("S11")
.state("S11")
.state("S12")
.and()
.withStates()
.parent("S0")
.state("S2")
.and()
.withStates()
.parent("S2")
.initial("S21")
.state("S21")
.and()
.withStates()
.parent("S21")
.initial("S211")
.state("S211")
.state("S212");
builder.configureTransitions()
.withExternal()
.source("S1").target("S1").event("A")
.guard(foo1Guard())
.and()
.withExternal()
.source("S1").target("S11").event("B")
.and()
.withExternal()
.source("S21").target("S211").event("B")
.and()
.withExternal()
.source("S1").target("S2").event("C")
.and()
.withExternal()
.source("S2").target("S1").event("C")
.and()
.withExternal()
.source("S1").target("S0").event("D")
.and()
.withExternal()
.source("S211").target("S21").event("D")
.and()
.withExternal()
.source("S0").target("S211").event("E")
.and()
.withExternal()
.source("S1").target("S211").event("F")
.and()
.withExternal()
.source("S2").target("S11").event("F")
.and()
.withExternal()
.source("S11").target("S211").event("G")
.and()
.withExternal()
.source("S211").target("S0").event("G")
.and()
.withInternal()
.source("S0").event("H")
.guard(foo0Guard())
.action(fooAction())
.and()
.withInternal()
.source("S2").event("H")
.guard(foo1Guard())
.action(fooAction())
.and()
.withInternal()
.source("S1").event("H")
.and()
.withExternal()
.source("S11").target("S12").event("I")
.and()
.withExternal()
.source("S211").target("S212").event("I")
.and()
.withExternal()
.source("S12").target("S212").event("I");
return builder.build();
}
private StateMachine<String, String> buildTestStateMachine2(CuratorFramework curatorClient)
throws Exception {
StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.taskExecutor(new SyncTaskExecutor())
.autoStartup(true)
.and()
.withDistributed()
.ensemble(stateMachineEnsemble(curatorClient));
builder.configureStates()
.withStates()
.initial("SI")
.state("S1")
.state("S2");
builder.configureTransitions()
.withExternal()
.source("SI").target("S1").event("E1")
.and()
.withExternal()
.source("S1").target("S2").event("E2")
.and()
.withExternal()
.source("S2").target("S1").event("E3")
.and()
.withInternal()
.source("SI").event("EV")
.action(setVariableAction())
.and()
.withInternal()
.source("S1").event("EV")
.action(setVariableAction());
return builder.build();
}
private static FooGuard foo0Guard() {
return new FooGuard(0);
}
private static FooGuard foo1Guard() {
return new FooGuard(1);
}
private static FooAction fooAction() {
return new FooAction();
}
private static SetVariableAction setVariableAction() {
return new SetVariableAction();
}
private static class FooGuard implements Guard<String, String> {
private final int match;
public FooGuard(int match) {
this.match = match;
}
@Override
public boolean evaluate(StateContext<String, String> context) {
Object foo = context.getExtendedState().getVariables().get("foo");
return !(foo == null || !foo.equals(match));
}
}
private static class FooAction implements Action<String, String> {
@Override
public void execute(StateContext<String, String> context) {
Map<Object, Object> variables = context.getExtendedState().getVariables();
Integer foo = context.getExtendedState().get("foo", Integer.class);
if (foo == null) {
variables.put("foo", 0);
} else if (foo == 0) {
variables.put("foo", 1);
} else if (foo == 1) {
variables.put("foo", 0);
}
}
}
private static class SetVariableAction implements Action<String, String> {
@Override
public void execute(StateContext<String, String> context) {
String testVariable = context.getMessageHeaders().get("testVariable", String.class);
if (testVariable != null) {
context.getExtendedState().getVariables().put("testVariable", testVariable);
}
}
}
}