/*
* 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.greaterThan;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.KeeperException;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachineException;
import org.springframework.statemachine.access.StateMachineAccessor;
import org.springframework.statemachine.ensemble.EnsembleListener;
import org.springframework.statemachine.ensemble.StateMachineEnsembleException;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.support.DefaultExtendedState;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import org.springframework.statemachine.transition.Transition;
public class ZookeeperStateMachineEnsembleTests extends AbstractZookeeperTests {
@Test
public void testInitStart() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
ZookeeperStateMachineEnsemble<String, String> ensemble =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
ensemble.afterPropertiesSet();
assertThat(curatorClient.checkExists().forPath("/foo/data/current"), notNullValue());
assertThat(curatorClient.checkExists().forPath("/foo/data/log"), notNullValue());
ensemble.start();
}
@Test
public void testPersist() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
ZookeeperStateMachineEnsemble<String, String> ensemble =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
ensemble.afterPropertiesSet();
assertThat(curatorClient.checkExists().forPath("/foo/data/current"), notNullValue());
assertThat(curatorClient.getData().forPath("/foo/data/current").length, is(0));
ensemble.setState(new DefaultStateMachineContext<String, String>("S1","E1", new HashMap<String, Object>(), new DefaultExtendedState()));
assertThat(curatorClient.getData().forPath("/foo/data/current").length, greaterThan(0));
ensemble.setState(new DefaultStateMachineContext<String, String>("S2","E1", new HashMap<String, Object>(), new DefaultExtendedState()));
}
@Test
public void testReadStateFromOther() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
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();
assertThat(curatorClient.checkExists().forPath("/foo/data/current"), notNullValue());
assertThat(curatorClient.getData().forPath("/foo/data/current").length, is(0));
ensemble1.setState(new DefaultStateMachineContext<String, String>("S1","E1", new HashMap<String, Object>(), new DefaultExtendedState()));
assertThat(curatorClient.getData().forPath("/foo/data/current").length, greaterThan(0));
StateMachineContext<String, String> context = ensemble2.getState();
assertThat(context, notNullValue());
assertThat(context.getState(), is("S1"));
assertThat(context.getEvent(), is("E1"));
}
@Test
public void testReceiveEvents() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
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");
TestEnsembleListener listener1 = new TestEnsembleListener();
TestEnsembleListener listener2 = new TestEnsembleListener();
ensemble1.addEnsembleListener(listener1);
ensemble2.addEnsembleListener(listener2);
ensemble1.afterPropertiesSet();
ensemble1.start();
ensemble2.afterPropertiesSet();
ensemble2.start();
TestStateMachine stateMachine1 = new TestStateMachine();
TestStateMachine stateMachine2 = new TestStateMachine();
ensemble1.join(stateMachine1);
ensemble2.join(stateMachine2);
assertThat(listener1.joinedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener2.joinedLatch.await(2, TimeUnit.SECONDS), is(true));
ensemble1.setState(new DefaultStateMachineContext<String, String>("S1", "E1", new HashMap<String, Object>(), new DefaultExtendedState()));
assertThat(listener2.eventLatch.await(2, TimeUnit.SECONDS), is(true));
}
@Test
public void testClearExistingStatePaths() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
// members base path need to exist for delete to happen
curatorClient.create().creatingParentsIfNeeded().forPath("/foo/members");
curatorClient.create().creatingParentsIfNeeded().forPath("/foo/data/log", new byte[10]);
ZookeeperStateMachineEnsemble<String, String> ensemble1 =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
ensemble1.afterPropertiesSet();
ensemble1.start();
// we assume that if data is 0, it's re-created
assertThat(curatorClient.getData().forPath("/foo/data/log").length, is(0));
}
@Test
public void testLogs() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
ZookeeperStateMachineEnsemble<String, String> ensemble =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo", true, 4);
ensemble.afterPropertiesSet();
ensemble.start();
assertThat(curatorClient.checkExists().forPath("/foo/data/log"), notNullValue());
assertThat(curatorClient.checkExists().forPath("/foo/data/log/0"), notNullValue());
assertThat(curatorClient.checkExists().forPath("/foo/data/log/1"), notNullValue());
assertThat(curatorClient.checkExists().forPath("/foo/data/log/2"), notNullValue());
assertThat(curatorClient.checkExists().forPath("/foo/data/log/3"), notNullValue());
assertThat(curatorClient.checkExists().forPath("/foo/data/log/4"), nullValue());
assertThat(curatorClient.getData().forPath("/foo/data/log/0").length, is(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/1").length, is(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/2").length, is(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/3").length, is(0));
ensemble.setState(new DefaultStateMachineContext<String, String>("S1","E1", new HashMap<String, Object>(), new DefaultExtendedState()));
assertThat(curatorClient.getData().forPath("/foo/data/log/0").length, greaterThan(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/1").length, is(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/2").length, is(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/3").length, is(0));
ensemble.setState(new DefaultStateMachineContext<String, String>("S2","E1", new HashMap<String, Object>(), new DefaultExtendedState()));
assertThat(curatorClient.getData().forPath("/foo/data/log/0").length, greaterThan(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/1").length, greaterThan(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/2").length, is(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/3").length, is(0));
ensemble.setState(new DefaultStateMachineContext<String, String>("S3","E1", new HashMap<String, Object>(), new DefaultExtendedState()));
assertThat(curatorClient.getData().forPath("/foo/data/log/0").length, greaterThan(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/1").length, greaterThan(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/2").length, greaterThan(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/3").length, is(0));
ensemble.setState(new DefaultStateMachineContext<String, String>("S4","E1", new HashMap<String, Object>(), new DefaultExtendedState()));
assertThat(curatorClient.getData().forPath("/foo/data/log/0").length, greaterThan(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/1").length, greaterThan(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/2").length, greaterThan(0));
assertThat(curatorClient.getData().forPath("/foo/data/log/3").length, greaterThan(0));
}
@Test(expected = IllegalStateException.class)
public void testIllegalLogSize() throws Exception {
new ZookeeperStateMachineEnsemble<String, String>(null, "/foo", true, 3);
}
@Test
public void testContextEventsNotMissedBurstNoOverflow() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
ZookeeperStateMachineEnsemble<String, String> ensemble =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
TestEnsembleListener listener = new TestEnsembleListener();
ensemble.addEnsembleListener(listener);
ensemble.afterPropertiesSet();
ensemble.start();
ensemble.join(new TestStateMachine());
assertThat(listener.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
listener.reset(0, 10);
for (int i = 0; i < 10; i++) {
ensemble.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
}
assertThat(listener.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener.events.size(), is(10));
for (int i = 0; i < 10; i++) {
assertThat(listener.events.get(i).getEvent(), is("E" + i));
}
}
@Test
public void testContextEventsNotMissedBurstNoOverflow2() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
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");
TestEnsembleListener listener1 = new TestEnsembleListener();
ensemble1.addEnsembleListener(listener1);
TestEnsembleListener listener2 = new TestEnsembleListener();
ensemble2.addEnsembleListener(listener2);
ensemble1.afterPropertiesSet();
ensemble1.start();
ensemble2.afterPropertiesSet();
ensemble2.start();
ensemble1.join(new TestStateMachine());
assertThat(listener1.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
ensemble2.join(new TestStateMachine());
assertThat(listener2.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
listener1.reset(0, 10);
listener2.reset(0, 10);
for (int i = 0; i < 10; i++) {
ensemble1.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
}
assertThat(listener1.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener1.events.size(), is(10));
assertThat(listener2.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener2.events.size(), is(10));
for (int i = 0; i < 10; i++) {
assertThat(listener1.events.get(i).getEvent(), is("E" + i));
assertThat(listener2.events.get(i).getEvent(), is("E" + i));
}
}
@Test
public void testContextEventsNotMissedBurstNoOverflow3() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
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");
TestEnsembleListener listener1 = new TestEnsembleListener();
ensemble1.addEnsembleListener(listener1);
TestEnsembleListener listener2 = new TestEnsembleListener();
ensemble2.addEnsembleListener(listener2);
ensemble1.afterPropertiesSet();
ensemble1.start();
ensemble2.afterPropertiesSet();
ensemble2.start();
ensemble1.join(new TestStateMachine());
assertThat(listener1.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
ensemble2.join(new TestStateMachine());
assertThat(listener2.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
listener1.reset(0, 10);
listener2.reset(0, 10);
Exception e = null;
try {
for (int i = 0; i < 10; i++) {
if (((i % 2) == 0)) {
ensemble1.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
} else {
ensemble2.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
}
}
} catch (Exception ee) {
e = ee;
}
if (e != null) {
assertThat(e, instanceOf(StateMachineException.class));
assertThat(((StateMachineException)e).contains(KeeperException.BadVersionException.class), is(true));
} else {
// miracle happened and no cas error, well then check events
assertThat(listener1.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener1.events.size(), is(10));
assertThat(listener2.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener2.events.size(), is(10));
for (int i = 0; i < 10; i++) {
assertThat(listener1.events.get(i).getEvent(), is("E" + i));
assertThat(listener2.events.get(i).getEvent(), is("E" + i));
}
}
}
@Test
public void testContextEventsNotMissedBurstNoOverflow4() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
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");
TestEnsembleListener listener1 = new TestEnsembleListener();
ensemble1.addEnsembleListener(listener1);
TestEnsembleListener listener2 = new TestEnsembleListener();
ensemble2.addEnsembleListener(listener2);
ensemble1.afterPropertiesSet();
ensemble1.start();
ensemble2.afterPropertiesSet();
ensemble2.start();
ensemble1.join(new TestStateMachine());
assertThat(listener1.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
ensemble2.join(new TestStateMachine());
assertThat(listener2.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
for (int i = 0; i < 10; i++) {
listener1.reset(0, 1);
listener2.reset(0, 1);
if (((i % 2) == 0)) {
ensemble1.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
} else {
ensemble2.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
}
assertThat(listener1.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener1.events.size(), is(1));
assertThat(listener2.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener2.events.size(), is(1));
}
}
@Test
public void testContextEventsNotMissedSlowNoOverflow() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
ZookeeperStateMachineEnsemble<String, String> ensemble =
new ZookeeperStateMachineEnsemble<String, String>(curatorClient, "/foo");
TestEnsembleListener listener = new TestEnsembleListener();
ensemble.addEnsembleListener(listener);
ensemble.afterPropertiesSet();
ensemble.start();
ensemble.join(new TestStateMachine());
assertThat(listener.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
listener.reset(0, 10);
for (int i = 0; i < 10; i++) {
ensemble.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
Thread.sleep(500);
}
assertThat(listener.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener.events.size(), is(10));
for (int i = 0; i < 10; i++) {
assertThat(listener.events.get(i).getEvent(), is("E" + i));
}
}
@Test
public void testContextEventsNotMissedSlowNoOverflow2() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
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");
TestEnsembleListener listener1 = new TestEnsembleListener();
TestEnsembleListener listener2 = new TestEnsembleListener();
ensemble1.addEnsembleListener(listener1);
ensemble2.addEnsembleListener(listener2);
ensemble1.afterPropertiesSet();
ensemble1.start();
ensemble2.afterPropertiesSet();
ensemble2.start();
ensemble1.join(new TestStateMachine());
assertThat(listener1.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
ensemble2.join(new TestStateMachine());
assertThat(listener2.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
listener1.reset(0, 10);
listener2.reset(0, 10);
for (int i = 0; i < 10; i++) {
ensemble1.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
Thread.sleep(500);
}
assertThat(listener1.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener1.events.size(), is(10));
assertThat(listener2.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener2.events.size(), is(10));
for (int i = 0; i < 10; i++) {
assertThat(listener1.events.get(i).getEvent(), is("E" + i));
assertThat(listener2.events.get(i).getEvent(), is("E" + i));
}
}
@Test
public void testDoesNotThrowCasError() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
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");
TestEnsembleListener listener1 = new TestEnsembleListener();
TestEnsembleListener listener2 = new TestEnsembleListener();
ensemble1.addEnsembleListener(listener1);
ensemble2.addEnsembleListener(listener2);
ensemble1.afterPropertiesSet();
ensemble1.start();
ensemble2.afterPropertiesSet();
ensemble2.start();
ensemble1.join(new TestStateMachine());
assertThat(listener1.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
ensemble2.join(new TestStateMachine());
assertThat(listener2.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
listener1.reset(0, 9);
listener2.reset(0, 9);
for (int i = 0; i < 9; i++) {
ensemble1.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
}
assertThat(listener1.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener1.events.size(), is(9));
assertThat(listener2.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener2.events.size(), is(9));
for (int i = 0; i < 9; i++) {
assertThat(listener1.events.get(i).getEvent(), is("E" + i));
assertThat(listener2.events.get(i).getEvent(), is("E" + i));
}
listener1.reset(0, 1);
listener2.reset(0, 1);
// should not throw BadVersionException when we immediately
for (int i = 9; i < 10; i++) {
ensemble2.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
}
assertThat(listener1.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener1.events.size(), is(1));
assertThat(listener2.eventLatch.await(10, TimeUnit.SECONDS), is(true));
assertThat(listener2.events.size(), is(1));
for (int i = 0; i < 1; i++) {
assertThat(listener1.events.get(i).getEvent(), is("E" + (i+9)));
assertThat(listener2.events.get(i).getEvent(), is("E" + (i+9)));
}
}
@Test
public void testEventsOverflow() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
OverflowControlZookeeperStateMachineEnsemble ensemble =
new OverflowControlZookeeperStateMachineEnsemble(curatorClient, "/foo", true, 4);
TestEnsembleListener listener = new TestEnsembleListener();
ensemble.addEnsembleListener(listener);
ensemble.afterPropertiesSet();
ensemble.start();
ensemble.join(new TestStateMachine());
assertThat(listener.joinedLatch.await(3, TimeUnit.SECONDS), is(true));
listener.reset(0, 10, 1);
// this is a bit of a hack to test things like this
// not sure if this is totally reliable way
// we've hacked to disable znode event registration so
// should not get any errors until it's re-enabled and
// we write again
for (int i = 0; i < 10; i++) {
ensemble.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
}
assertThat(listener.errorLatch.await(2, TimeUnit.SECONDS), is(false));
ensemble.enabled = true;
// logging error if this fails
TestUtils.callMethod("registerWatcherForStatePath", ensemble);
String reason = "";
if (listener.errors.size() > 0) {
reason = listener.errors.get(0).toString();
}
assertThat(reason, listener.errors.size(), is(0));
// this should actually cause ensemble to fail
for (int i = 10; i < 11; i++) {
ensemble.setState(new DefaultStateMachineContext<String, String>("S" + i, "E" + i,
new HashMap<String, Object>(), new DefaultExtendedState()));
}
assertThat(listener.errorLatch.await(2, TimeUnit.SECONDS), is(true));
}
private class OverflowControlZookeeperStateMachineEnsemble extends ZookeeperStateMachineEnsemble<String, String> {
boolean enabled = false;
public OverflowControlZookeeperStateMachineEnsemble(CuratorFramework curatorClient, String basePath,
boolean cleanState, int logSize) {
super(curatorClient, basePath, cleanState, logSize);
}
@Override
protected void registerWatcherForStatePath() {
if (enabled) {
super.registerWatcherForStatePath();
}
}
}
@Override
protected AnnotationConfigApplicationContext buildContext() {
return new AnnotationConfigApplicationContext();
}
private class TestEnsembleListener implements EnsembleListener<String, String> {
volatile CountDownLatch joinedLatch = new CountDownLatch(1);
volatile CountDownLatch eventLatch = new CountDownLatch(1);
volatile CountDownLatch errorLatch = new CountDownLatch(1);
volatile List<Exception> errors = new ArrayList<Exception>();
volatile List<StateMachineContext<String, String>> events = new ArrayList<StateMachineContext<String,String>>();
@Override
public void stateMachineJoined(StateMachine<String, String> stateMachine, StateMachineContext<String, String> context) {
joinedLatch.countDown();
}
@Override
public void stateMachineLeft(StateMachine<String, String> stateMachine, StateMachineContext<String, String> context) {
}
@Override
public void stateChanged(StateMachineContext<String, String> context) {
events.add(context);
eventLatch.countDown();
}
@Override
public void ensembleError(StateMachineEnsembleException exception) {
errors.add(exception);
errorLatch.countDown();
}
@Override
public void ensembleLeaderGranted(StateMachine<String, String> stateMachine) {
}
@Override
public void ensembleLeaderRevoked(StateMachine<String, String> stateMachine) {
}
public void reset(int c1, int c2) {
reset(c1, c2, 0);
}
public void reset(int c1, int c2, int c3) {
joinedLatch = new CountDownLatch(c1);
eventLatch = new CountDownLatch(c2);
errorLatch = new CountDownLatch(c3);
events.clear();
errors.clear();
}
}
private class TestStateMachine implements StateMachine<String, String> {
@Override
public StateMachineAccessor<String, String> getStateMachineAccessor() {
return null;
}
@Override
public void start() {
}
@Override
public void stop() {
}
@Override
public boolean sendEvent(Message<String> event) {
return false;
}
@Override
public boolean sendEvent(String event) {
return false;
}
@Override
public State<String, String> getState() {
return null;
}
@Override
public Collection<State<String, String>> getStates() {
return null;
}
@Override
public Collection<Transition<String, String>> getTransitions() {
return null;
}
@Override
public boolean isComplete() {
return false;
}
@Override
public void setStateMachineError(Exception exception) {
}
@Override
public boolean hasStateMachineError() {
return false;
}
@Override
public void addStateListener(StateMachineListener<String, String> listener) {
}
@Override
public void removeStateListener(StateMachineListener<String, String> listener) {
}
@Override
public State<String, String> getInitialState() {
return null;
}
@Override
public ExtendedState getExtendedState() {
return null;
}
@Override
public UUID getUuid() {
return null;
}
@Override
public String getId() {
return null;
}
}
}