diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java index eb39c27c..dc48f3bb 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java @@ -51,4 +51,18 @@ public interface StateMachine extends Region { */ StateMachineAccessor getStateMachineAccessor(); + /** + * Sets the state machine error. + * + * @param exception the new state machine error + */ + void setStateMachineError(Exception exception); + + /** + * Checks for state machine error. + * + * @return true, if error has been set + */ + boolean hasStateMachineError(); + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/ensemble/DistributedStateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/ensemble/DistributedStateMachine.java index 552f01d3..adc354f8 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/ensemble/DistributedStateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/ensemble/DistributedStateMachine.java @@ -133,6 +133,16 @@ public class DistributedStateMachine extends LifecycleObjectSupport implem return delegate.isComplete(); } + @Override + public void setStateMachineError(Exception exception) { + delegate.setStateMachineError(exception); + } + + @Override + public boolean hasStateMachineError() { + return delegate.hasStateMachineError(); + } + @Override public void addStateListener(StateMachineListener listener) { delegate.addStateListener(listener); @@ -215,6 +225,11 @@ public class DistributedStateMachine extends LifecycleObjectSupport implem return stateContext; } + @Override + public Exception stateMachineError(StateMachine stateMachine, Exception exception) { + return exception; + } + } /** @@ -269,9 +284,8 @@ public class DistributedStateMachine extends LifecycleObjectSupport implem @Override public void ensembleError(StateMachineEnsembleException exception) { - // TODO: when we get support for sm error handling, - // propagate this exception there log.error("Ensemble error", exception); + setStateMachineError(exception); throw exception; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/DefaultStateMachineEventPublisher.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/DefaultStateMachineEventPublisher.java index 2ee2efd2..4e670ea5 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/DefaultStateMachineEventPublisher.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/DefaultStateMachineEventPublisher.java @@ -115,4 +115,11 @@ public class DefaultStateMachineEventPublisher implements StateMachineEventPubli } } + @Override + public void publishStateMachineError(Object source, StateMachine stateMachine, Exception exception) { + if (applicationEventPublisher != null) { + applicationEventPublisher.publishEvent(new OnStateMachineError(source, stateMachine, exception)); + } + } + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnStateMachineError.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnStateMachineError.java new file mode 100644 index 00000000..d55d46a0 --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnStateMachineError.java @@ -0,0 +1,69 @@ +/* + * 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.event; + +import org.springframework.statemachine.StateMachine; + +/** + * Generic event representing that state machine has been entered in + * error it cannot recover. + * + * @author Janne Valkealahti + * + */ +@SuppressWarnings("serial") +public class OnStateMachineError extends StateMachineEvent { + + private final StateMachine stateMachine; + private final Exception exception; + + /** + * Instantiates a new on state exit event. + * + * @param source the source + * @param stateMachine the statemachine + * @param exception the exception + */ + public OnStateMachineError(Object source, StateMachine stateMachine, Exception exception) { + super(source); + this.stateMachine = stateMachine; + this.exception = exception; + } + + /** + * Gets the statemachine. + * + * @return the statemachine + */ + public StateMachine getStateMachine() { + return stateMachine; + } + + /** + * Gets the exception. + * + * @return the exception + */ + public Exception getException() { + return exception; + } + + @Override + public String toString() { + return "OnStateMachineError [stateMachine=" + stateMachine + ", exception=" + exception + "]"; + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEventPublisher.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEventPublisher.java index 33d5ca20..bd717a78 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEventPublisher.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEventPublisher.java @@ -101,4 +101,13 @@ public interface StateMachineEventPublisher { */ void publishStateMachineStop(Object source, StateMachine stateMachine); + /** + * Publish a state machine error. + * + * @param source the source + * @param stateMachine the state machine + * @param exception the exception + */ + void publishStateMachineError(Object source, StateMachine stateMachine, Exception exception); + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/CompositeStateMachineListener.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/CompositeStateMachineListener.java index d45a9c0e..328ef7c5 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/CompositeStateMachineListener.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/CompositeStateMachineListener.java @@ -105,4 +105,12 @@ public class CompositeStateMachineListener extends AbstractCompositeListene } } + @Override + public void stateMachineError(StateMachine stateMachine, Exception exception) { + for (Iterator> iterator = getListeners().reverse(); iterator.hasNext();) { + StateMachineListener listener = iterator.next(); + listener.stateMachineError(stateMachine, exception); + } + } + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListener.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListener.java index c026fdb1..65919734 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListener.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListener.java @@ -94,4 +94,12 @@ public interface StateMachineListener { */ void stateMachineStopped(StateMachine stateMachine); + /** + * Notified when statemachine enters error it can't recover from. + * + * @param stateMachine the state machine + * @param exception the exception + */ + void stateMachineError(StateMachine stateMachine, Exception exception); + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListenerAdapter.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListenerAdapter.java index 7de85f75..b200bdf0 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListenerAdapter.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListenerAdapter.java @@ -67,4 +67,8 @@ public class StateMachineListenerAdapter implements StateMachineListener stateMachine) { } + @Override + public void stateMachineError(StateMachine stateMachine, Exception exception) { + } + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java index 4797a9a1..74e42a84 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java @@ -92,6 +92,8 @@ public abstract class AbstractStateMachine extends StateMachineObjectSuppo private volatile State currentState; + private volatile Exception currentError; + private volatile PseudoState history; private final Map> handlers = new HashMap>(); @@ -180,6 +182,11 @@ public abstract class AbstractStateMachine extends StateMachineObjectSuppo @Override public boolean sendEvent(Message event) { + if (hasStateMachineError()) { + // TODO: should we throw exception? + notifyEventNotAccepted(event); + return false; + } if (isComplete() || !isRunning()) { notifyEventNotAccepted(event); return false; @@ -292,6 +299,24 @@ public abstract class AbstractStateMachine extends StateMachineObjectSuppo initialEnabled = null; } + @Override + public void setStateMachineError(Exception exception) { + if (exception == null) { + currentError = null; + } else { + exception = getStateMachineInterceptors().stateMachineError(this, exception); + currentError = exception; + } + if (currentError != null) { + notifyStateMachineError(this, currentError); + } + } + + @Override + public boolean hasStateMachineError() { + return currentError != null; + } + @Override public void addStateListener(StateMachineListener listener) { getStateListener().register(listener); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptor.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptor.java index c95cc8cd..5aa6264c 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptor.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptor.java @@ -73,4 +73,13 @@ public interface StateMachineInterceptor { */ StateContext postTransition(StateContext stateContext); + /** + * State when state machine is about to enter error it can't recover. + * + * @param stateMachine the state machine + * @param exception the exception + * @return the exception + */ + Exception stateMachineError(StateMachine stateMachine, Exception exception); + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptorAdapter.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptorAdapter.java index 15f2a73a..3e4a6619 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptorAdapter.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptorAdapter.java @@ -51,4 +51,9 @@ public class StateMachineInterceptorAdapter implements StateMachineInterce return stateContext; } + @Override + public Exception stateMachineError(StateMachine stateMachine, Exception exception) { + return exception; + } + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptorList.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptorList.java index 7aab7b0d..02f19669 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptorList.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineInterceptorList.java @@ -131,4 +131,20 @@ public class StateMachineInterceptorList { return stateContext; } + /** + * State machine error. + * + * @param stateMachine the state machine + * @param exception the exception + * @return the exception + */ + public Exception stateMachineError(StateMachine stateMachine, Exception exception) { + for (StateMachineInterceptor interceptor : interceptors) { + if ((exception = interceptor.stateMachineError(stateMachine, exception)) == null) { + break; + } + } + return exception; + } + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineObjectSupport.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineObjectSupport.java index 5d1a053d..3a7f055c 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineObjectSupport.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineObjectSupport.java @@ -183,6 +183,16 @@ public abstract class StateMachineObjectSupport extends LifecycleObjectSup } } + protected void notifyStateMachineError(StateMachine stateMachine, Exception exception) { + stateListener.stateMachineError(stateMachine, exception); + if (contextEventsEnabled) { + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishStateMachineError(this, stateMachine, exception); + } + } + } + protected void stateChangedInRelay() { // TODO: this is a temporary tweak to know when state is // changed in a submachine/regions order to give @@ -255,6 +265,11 @@ public abstract class StateMachineObjectSupport extends LifecycleObjectSup stateListener.stateMachineStopped(stateMachine); } + @Override + public void stateMachineError(StateMachine stateMachine, Exception exception) { + stateListener.stateMachineError(stateMachine, exception); + } + } } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineErrorTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineErrorTests.java new file mode 100644 index 00000000..a52cc97e --- /dev/null +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineErrorTests.java @@ -0,0 +1,214 @@ +/* + * 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; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.access.StateMachineAccess; +import org.springframework.statemachine.access.StateMachineFunction; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.event.OnStateMachineError; +import org.springframework.statemachine.event.StateMachineEvent; +import org.springframework.statemachine.listener.StateMachineListenerAdapter; +import org.springframework.statemachine.support.StateMachineInterceptorAdapter; + +/** + * Tests for various errors and error handling. + * + * @author Janne Valkealahti + * + */ +public class StateMachineErrorTests extends AbstractStateMachineTests { + + @Override + protected AnnotationConfigApplicationContext buildContext() { + return new AnnotationConfigApplicationContext(); + } + + @Test + public void testEvents() throws Exception { + context.register(Config.class, Config1.class); + context.refresh(); + + TestApplicationEventListener listener1 = context.getBean(TestApplicationEventListener.class); + + @SuppressWarnings("unchecked") + ObjectStateMachine machine = + context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class); + + assertThat(machine.hasStateMachineError(), is(false)); + + TestStateMachineListener listener2 = new TestStateMachineListener(); + machine.addStateListener(listener2); + + machine.start(); + machine.setStateMachineError(new RuntimeException("myerror")); + + assertThat(listener1.latch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(listener1.count, is(1)); + assertThat(listener2.latch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(listener2.count, is(1)); + assertThat(machine.hasStateMachineError(), is(true)); + } + + @Test + public void testInterceptHandlesError() throws Exception { + context.register(Config.class, Config1.class); + context.refresh(); + + TestApplicationEventListener listener1 = context.getBean(TestApplicationEventListener.class); + + @SuppressWarnings("unchecked") + ObjectStateMachine machine = + context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class); + + assertThat(machine.hasStateMachineError(), is(false)); + + machine.getStateMachineAccessor().doWithRegion(new StateMachineFunction>() { + + @Override + public void apply(StateMachineAccess function) { + function.addStateMachineInterceptor(new StateMachineInterceptorAdapter() { + @Override + public Exception stateMachineError(StateMachine stateMachine, + Exception exception) { + return null; + } + }); + } + }); + + TestStateMachineListener listener2 = new TestStateMachineListener(); + machine.addStateListener(listener2); + + machine.start(); + machine.setStateMachineError(new RuntimeException("myerror")); + + assertThat(listener1.latch.await(1, TimeUnit.SECONDS), is(false)); + assertThat(listener1.count, is(0)); + assertThat(listener2.latch.await(1, TimeUnit.SECONDS), is(false)); + assertThat(listener2.count, is(0)); + assertThat(machine.hasStateMachineError(), is(false)); + } + + @Test + public void testErrorActive() throws Exception { + context.register(Config1.class); + context.refresh(); + + @SuppressWarnings("unchecked") + ObjectStateMachine machine = + context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class); + + assertThat(machine.hasStateMachineError(), is(false)); + machine.start(); + machine.setStateMachineError(new RuntimeException("myerror")); + assertThat(machine.hasStateMachineError(), is(true)); + assertThat(machine.getState().getIds(), containsInAnyOrder(TestStates.S1)); + machine.sendEvent(TestEvents.E1); + assertThat(machine.getState().getIds(), containsInAnyOrder(TestStates.S1)); + } + + + @Configuration + @EnableStateMachine + static class Config1 extends EnumStateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial(TestStates.S1) + .state(TestStates.S2) + .state(TestStates.S3) + .state(TestStates.S4); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions + .withExternal() + .source(TestStates.S1) + .target(TestStates.S2) + .event(TestEvents.E1) + .and() + .withExternal() + .source(TestStates.S2) + .target(TestStates.S3) + .event(TestEvents.E2) + .and() + .withExternal() + .source(TestStates.S3) + .target(TestStates.S4) + .event(TestEvents.E3) + .and() + .withExternal() + .source(TestStates.S4) + .target(TestStates.S3) + .event(TestEvents.E4); + } + + } + + @Configuration + static class Config { + + @Bean + public TestApplicationEventListener testApplicationEventListener() { + return new TestApplicationEventListener(); + } + } + + static class TestStateMachineListener extends StateMachineListenerAdapter { + + CountDownLatch latch = new CountDownLatch(1); + int count = 0; + + @Override + public void stateMachineError(StateMachine stateMachine, Exception exception) { + count++; + latch.countDown(); + } + } + + static class TestApplicationEventListener implements ApplicationListener { + + CountDownLatch latch = new CountDownLatch(1); + int count = 0; + + @Override + public void onApplicationEvent(StateMachineEvent event) { + if (event instanceof OnStateMachineError) { + count++; + latch.countDown(); + } + } + } + +} diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/access/StateMachineAccessTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/access/StateMachineAccessTests.java index a32797ca..570cd15a 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/access/StateMachineAccessTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/access/StateMachineAccessTests.java @@ -142,6 +142,15 @@ public class StateMachineAccessTests { return false; } + @Override + public void setStateMachineError(Exception exception) { + } + + @Override + public boolean hasStateMachineError() { + return false; + } + @Override public void addStateListener(StateMachineListener listener) { } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/docs/DocsConfigurationSampleTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/docs/DocsConfigurationSampleTests.java index eded8c56..51e1552c 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/docs/DocsConfigurationSampleTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/docs/DocsConfigurationSampleTests.java @@ -866,6 +866,12 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests { public void postStateChange(State state, Message message, Transition transition, StateMachine stateMachine) { } + + @Override + public Exception stateMachineError(StateMachine stateMachine, + Exception exception) { + return exception; + } }); // end::snippetZH[] } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java index 0fc53293..24ec5a20 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java @@ -176,6 +176,10 @@ public class ListenerTests extends AbstractStateMachineTests { stopLatch.countDown(); } + @Override + public void stateMachineError(StateMachine stateMachine, Exception exception) { + } + } @Configuration diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateChangeInterceptorTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateChangeInterceptorTests.java index 7920193c..8527b398 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateChangeInterceptorTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateChangeInterceptorTests.java @@ -301,6 +301,11 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests { preStateChangeCount = 0; } + @Override + public Exception stateMachineError(StateMachine stateMachine, Exception exception) { + return exception; + } + } } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateContextExpressionMethodsTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateContextExpressionMethodsTests.java index 038f1acf..9fea64ac 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateContextExpressionMethodsTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateContextExpressionMethodsTests.java @@ -176,6 +176,15 @@ public class StateContextExpressionMethodsTests { return false; } + @Override + public void setStateMachineError(Exception exception) { + } + + @Override + public boolean hasStateMachineError() { + return false; + } + @Override public void addStateListener(StateMachineListener listener) { } diff --git a/spring-statemachine-zookeeper/src/test/java/org/springframework/statemachine/zookeeper/ZookeeperStateMachineEnsembleTests.java b/spring-statemachine-zookeeper/src/test/java/org/springframework/statemachine/zookeeper/ZookeeperStateMachineEnsembleTests.java index d294224b..bd766143 100644 --- a/spring-statemachine-zookeeper/src/test/java/org/springframework/statemachine/zookeeper/ZookeeperStateMachineEnsembleTests.java +++ b/spring-statemachine-zookeeper/src/test/java/org/springframework/statemachine/zookeeper/ZookeeperStateMachineEnsembleTests.java @@ -651,6 +651,15 @@ public class ZookeeperStateMachineEnsembleTests extends AbstractZookeeperTests { return false; } + @Override + public void setStateMachineError(Exception exception) { + } + + @Override + public boolean hasStateMachineError() { + return false; + } + @Override public void addStateListener(StateMachineListener listener) { }