diff --git a/docs/src/reference/asciidoc/appendix.adoc b/docs/src/reference/asciidoc/appendix.adoc index 8dbd8965..a6ab569a 100644 --- a/docs/src/reference/asciidoc/appendix.adoc +++ b/docs/src/reference/asciidoc/appendix.adoc @@ -310,5 +310,8 @@ in a state machine. This appendix provides more detailed technical documentation about using a Zookeeper with a Spring State Machine. -tbd. - +[NOTE] +==== +This article is not complete as it requires jepsen tests which are +planned for next release. +==== diff --git a/docs/src/reference/asciidoc/sm.adoc b/docs/src/reference/asciidoc/sm.adoc index d54d2912..6eaafb48 100644 --- a/docs/src/reference/asciidoc/sm.adoc +++ b/docs/src/reference/asciidoc/sm.adoc @@ -609,6 +609,52 @@ exposed directly via `StateMachine` interface. include::samples/DocsConfigurationSampleTests.java[tags=snippetZH] ---- +[NOTE] +==== +More about error handling shown in above example, see section +<>. +==== + +[[sm-error-handling]] +== State Machine Error Handling +If state machine detects an internal error during a state transition +logic it may throw an exception. Before this exception is processed +internally, user is given a change to intercept. + +Normal `StateMachineInterceptor` can be used to intercept errors and +example of it is shown above. +[source,java,indent=0] +---- +include::samples/DocsConfigurationSampleTests.java[tags=snippet1] +---- + +When errors are detected, normal event notify mechanism is executed. +This allows to use either `StateMachineListener` or Spring Application +context event listener, more about these read section +<>. + +Having said that, a simple listener would look like: + +[source,java,indent=0] +---- +include::samples/DocsConfigurationSampleTests.java[tags=snippet2] +---- + +Generic `ApplicationListener` checking `StateMachineEvent` would look +like. + +[source,java,indent=0] +---- +include::samples/DocsConfigurationSampleTests.java[tags=snippet3] +---- + +It's also possible to define `ApplicationListener` directly to +recognize only `StateMachineEvent` instances. + +[source,java,indent=0] +---- +include::samples/DocsConfigurationSampleTests.java[tags=snippet4] +---- [[sm-persist]] == Persisting State Machine 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 index a52cc97e..751a2f93 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineErrorTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineErrorTests.java @@ -56,7 +56,8 @@ public class StateMachineErrorTests extends AbstractStateMachineTests { context.register(Config.class, Config1.class); context.refresh(); - TestApplicationEventListener listener1 = context.getBean(TestApplicationEventListener.class); + TestApplicationEventListener1 listener1 = context.getBean(TestApplicationEventListener1.class); + TestApplicationEventListener2 listener3 = context.getBean(TestApplicationEventListener2.class); @SuppressWarnings("unchecked") ObjectStateMachine machine = @@ -72,6 +73,8 @@ public class StateMachineErrorTests extends AbstractStateMachineTests { assertThat(listener1.latch.await(1, TimeUnit.SECONDS), is(true)); assertThat(listener1.count, is(1)); + assertThat(listener3.latch.await(1, TimeUnit.SECONDS), is(true)); + assertThat(listener3.count, is(1)); assertThat(listener2.latch.await(1, TimeUnit.SECONDS), is(true)); assertThat(listener2.count, is(1)); assertThat(machine.hasStateMachineError(), is(true)); @@ -82,7 +85,7 @@ public class StateMachineErrorTests extends AbstractStateMachineTests { context.register(Config.class, Config1.class); context.refresh(); - TestApplicationEventListener listener1 = context.getBean(TestApplicationEventListener.class); + TestApplicationEventListener1 listener1 = context.getBean(TestApplicationEventListener1.class); @SuppressWarnings("unchecked") ObjectStateMachine machine = @@ -180,8 +183,13 @@ public class StateMachineErrorTests extends AbstractStateMachineTests { static class Config { @Bean - public TestApplicationEventListener testApplicationEventListener() { - return new TestApplicationEventListener(); + public TestApplicationEventListener1 testApplicationEventListener1() { + return new TestApplicationEventListener1(); + } + + @Bean + public TestApplicationEventListener2 testApplicationEventListener2() { + return new TestApplicationEventListener2(); } } @@ -197,7 +205,7 @@ public class StateMachineErrorTests extends AbstractStateMachineTests { } } - static class TestApplicationEventListener implements ApplicationListener { + static class TestApplicationEventListener1 implements ApplicationListener { CountDownLatch latch = new CountDownLatch(1); int count = 0; @@ -211,4 +219,17 @@ public class StateMachineErrorTests extends AbstractStateMachineTests { } } + static class TestApplicationEventListener2 implements ApplicationListener { + + CountDownLatch latch = new CountDownLatch(1); + int count = 0; + + @Override + public void onApplicationEvent(OnStateMachineError event) { + count++; + latch.countDown(); + } + + } + } 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 51e1552c..86349a3a 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 @@ -56,11 +56,13 @@ import org.springframework.statemachine.config.builders.StateMachineStateConfigu import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; import org.springframework.statemachine.config.configurers.StateConfigurer.History; import org.springframework.statemachine.ensemble.StateMachineEnsemble; +import org.springframework.statemachine.event.OnStateMachineError; import org.springframework.statemachine.event.StateMachineEvent; import org.springframework.statemachine.guard.Guard; import org.springframework.statemachine.listener.StateMachineListenerAdapter; import org.springframework.statemachine.state.State; import org.springframework.statemachine.support.StateMachineInterceptor; +import org.springframework.statemachine.support.StateMachineInterceptorAdapter; import org.springframework.statemachine.transition.Transition; /** @@ -878,4 +880,67 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests { } + @SuppressWarnings("unused") + private static class InterceptorAddExample { + +// tag::snippet1[] + StateMachine stateMachine; + + void addInterceptor() { + stateMachine.getStateMachineAccessor() + .doWithRegion(new StateMachineFunction>() { + + @Override + public void apply(StateMachineAccess function) { + function.addStateMachineInterceptor( + new StateMachineInterceptorAdapter() { + @Override + public Exception stateMachineError(StateMachine stateMachine, + Exception exception) { + // return null indicating handled error + return exception; + } + }); + } + }); + + } +// end::snippet1[] + } + +// tag::snippet2[] + public static class ErrorStateMachineListener + extends StateMachineListenerAdapter { + + @Override + public void stateMachineError(StateMachine stateMachine, Exception exception) { + // do something with error + } + } +// end::snippet2[] + +// tag::snippet3[] + public static class GenericApplicationEventListener + implements ApplicationListener { + + @Override + public void onApplicationEvent(StateMachineEvent event) { + if (event instanceof OnStateMachineError) { + // do something with error + } + } + } +// end::snippet3[] + +// tag::snippet4[] + public static class ErrorApplicationEventListener + implements ApplicationListener { + + @Override + public void onApplicationEvent(OnStateMachineError event) { + // do something with error + } + } +// end::snippet4[] + }