{"id":55965,"date":"2022-12-09T10:20:08","date_gmt":"2022-12-09T04:50:08","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=55965"},"modified":"2022-12-09T10:20:08","modified_gmt":"2022-12-09T04:50:08","slug":"spring-state-machine","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/spring-state-machine\/","title":{"rendered":"Spring State Machine"},"content":{"rendered":"<h2><strong class=\"ih jd\">State Machine Overview<\/strong><\/h2>\n<p id=\"ad87\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Spring state machine is a spring-module that allows us to describe well known, well understood transitions from one state to another. You have probably built a million state machines in your life if you have ever done any kind of programming. For example any kind of if-then-else statement at some point will constitute a state machine. So, any transition which is predictable, or deterministic from one state to another which may have actions associated with it is a state machine and is a very fundamental part of what we do.<\/p>\n<p id=\"b9a4\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">But things can go ugly with these if-else statements, if state transitions just become a little complex where a developer will get lost in the nested structure of if-else statements instead of solving a business problem. And to make life worse, if workflow has changes, the developer needs to scour different layers of the codebase where all complex decisions reside.<\/p>\n<p id=\"969d\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">State machines are a nice way to think about the progressions of our applications, how things should happen, and how they should move. Modelling your business project in terms of the state machine is useful even if you don\u2019t plan to. State machines describe state over a long running process, so if you have a long-running process with lots of moving parts, the state machine comes into picture.<\/p>\n<p id=\"ac31\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">State machine has the benefit of being queryable. You can interrogate where we are in the fulfilment flow. For example, I am fulfilling an order and we have a number of steps involved from tons of actors. Being able to poke at the state machine to check where we are and where we will proceed after the actor performs any deterministic action.<\/p>\n<p data-selectable-paragraph=\"\"><strong class=\"ih jd\">We will be building the State machine using the spring state machine project.<\/strong><\/p>\n<p data-selectable-paragraph=\"\">\u00a0<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-55964 size-full\" src=\"\/blog\/wp-ttn-blog\/uploads\/2022\/12\/Screenshot-2022-12-07-at-10.30.37-AM.png\" alt=\"\" width=\"2164\" height=\"1032\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2022\/12\/Screenshot-2022-12-07-at-10.30.37-AM.png 2164w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/Screenshot-2022-12-07-at-10.30.37-AM-300x143.png 300w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/Screenshot-2022-12-07-at-10.30.37-AM-1024x488.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/Screenshot-2022-12-07-at-10.30.37-AM-768x366.png 768w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/Screenshot-2022-12-07-at-10.30.37-AM-1536x733.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/Screenshot-2022-12-07-at-10.30.37-AM-2048x977.png 2048w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/Screenshot-2022-12-07-at-10.30.37-AM-624x298.png 624w\" sizes=\"(max-width: 2164px) 100vw, 2164px\" \/><\/p>\n<h2 id=\"50c6\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\"><strong class=\"ih jd\">Step 1<\/strong><\/h2>\n<p id=\"ba42\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">We will be building a gradle project with the following build.gradle.<\/p>\n<pre> plugins {\r\n id <span class=\"hljs-string\">'java'<\/span>\r\n id <span class=\"hljs-string\">'org.springframework.boot'<\/span> version <span class=\"hljs-string\">'3.0.0'<\/span>\r\n id <span class=\"hljs-string\">'io.spring.dependency-management'<\/span> version <span class=\"hljs-string\">'1.1.0'<\/span>\r\n }\r\n\r\n group = <span class=\"hljs-string\">'com.ttn'<\/span>\r\n version = <span class=\"hljs-string\">'0.0.1-SNAPSHOT'<\/span>\r\n sourceCompatibility = <span class=\"hljs-string\">'17'<\/span>\r\n\r\n configurations {\r\n compileOnly {\r\n extendsFrom annotationProcessor\r\n }\r\n }\r\n\r\n repositories {\r\n   mavenCentral()\r\n }\r\n\r\n dependencies {\r\n   implementation <span class=\"hljs-string\">'org.springframework.boot:spring-boot-starter-data-jpa'<\/span>\r\n   implementation <span class=\"hljs-string\">'org.springframework.boot:spring-boot-starter-web'<\/span>\r\n   compileOnly <span class=\"hljs-string\">'org.projectlombok:lombok'<\/span>\r\n   runtimeOnly <span class=\"hljs-string\">'com.mysql:mysql-connector-j'<\/span>\r\n   annotationProcessor <span class=\"hljs-string\">'org.projectlombok:lombok'<\/span>\r\n   testImplementation <span class=\"hljs-string\">'org.springframework.boot:spring-boot-starter-test'<\/span>\r\n   implementation group: <span class=\"hljs-string\">'org.springframework.statemachine'<\/span>, name: <span class=\"hljs-string\">'spring-statemachine-core'<\/span>, version: <span class=\"hljs-string\">'3.2.0'<\/span>\r\n   }<\/pre>\n<h2 id=\"5f9a\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\"><strong class=\"ih jd\">Step 2<\/strong><\/h2>\n<p id=\"cd33\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Let\u2019s define the states of Order<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"faf4\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">enum<\/span> <span class=\"hljs-title.class\">OrderStates<\/span> {\r\n   SUBMITTED, PAID, FULFILLED, CANCELED\r\n}<\/span><\/pre>\n<h2 id=\"dc05\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\"><strong class=\"ih jd\">Step 3<\/strong><\/h2>\n<p id=\"8408\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Let\u2019s create some Events<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"1bbe\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">enum<\/span> <span class=\"hljs-title.class\">OrderEvents<\/span> {\r\n   PAY, FULFILL, CANCEL;\r\n}<\/span><\/pre>\n<p id=\"4747\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Our state are changing like below:<\/p>\n<p class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><span style=\"font-style: italic; font-size: 1rem;\">1. When the order is created it will be in SUBMITTED state.<\/span><\/p>\n<p class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><span style=\"font-style: italic; font-size: 1rem;\">2. From there, if PAY event happens, state will change to PAID<\/span><\/p>\n<p class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><span style=\"font-style: italic; font-size: 1rem;\">3. From PAID, if FULFILL event happens, state will change to FULFILLED<\/span><\/p>\n<p class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><span style=\"font-style: italic; font-size: 1rem;\">4. FULFILLED state is the end state<\/span><\/p>\n<p class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><span style=\"font-style: italic; font-size: 1rem;\">5. But from FULFILLED, if CANCEL event happens, state will change to CANCELED<\/span><\/p>\n<p class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><span style=\"font-style: italic; font-size: 1rem;\">6. Also, from PAID, if CANCEL event happens, state will change to CANCELED<\/span><\/p>\n<p class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><span style=\"font-style: italic; font-size: 1rem;\">7. CANCELED state is also the end state<\/span><\/p>\n<p id=\"3643\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">We need to cover the above transition. First we create a State machine with Order States.<\/p>\n<p id=\"c9ce\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><strong class=\"ih jd\">Step 4:<\/strong><\/p>\n<p id=\"4cf5\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Configuring Spring State Machine<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"8926\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\"> @Slf4j<\/span>\r\n<span class=\"hljs-meta\"> @Configuration<\/span>\r\n<span class=\"hljs-meta\"> @EnableStateMachineFactory<\/span>\r\n<span class=\"hljs-keyword\"> class<\/span> <span class=\"hljs-title.class\">SimpleStateMachineConfiguration<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title.class\">StateMachineConfigurerAdapter<\/span>&lt;OrderStates,OrderEvents&gt; {..<\/span><\/pre>\n<p id=\"3ffa\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">We need to\u00a0<strong class=\"ih jd\">override<\/strong> methods of the above\u00a0<strong class=\"ih jd\">StateMachineConfigurerAdapter<\/strong>\u00a0to configure our state machine<\/p>\n<p id=\"1f98\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><strong class=\"ih jd\">The below override method helps us define the states of the machine<\/strong><\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"4a5a\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\"> @Override<\/span>\r\n<span class=\"hljs-keyword\"> public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title.function\">configure<\/span><span class=\"hljs-params\">(StateMachineStateConfigurer&lt;OrderStates, OrderEvents&gt; states)<\/span> <span class=\"hljs-keyword\">throws<\/span> Exception {\r\n    states.withStates()\r\n            .initial(OrderStates.SUBMITTED)\r\n            .state(OrderStates.PAID)\r\n            .end(OrderStates.FULFILLED)\r\n            .end(OrderStates.CANCELLED);\r\n }<\/span><\/pre>\n<p id=\"3922\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><strong class=\"ih jd\">The below override method help us to define the transitions we have discussed above<\/strong><\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"5702\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\"> @Override<\/span>\r\n<span class=\"hljs-keyword\"> public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title.function\">configure<\/span><span class=\"hljs-params\">(StateMachineTransitionConfigurer&lt;OrderStates,\r\n       OrderEvents&gt; transitions)<\/span> <span class=\"hljs-keyword\">throws<\/span> Exception {\r\n   transitions\r\n           .withExternal()\r\n           .source(OrderStates.SUBMITTED)\r\n           .target(OrderStates.PAID)\r\n           .event(OrderEvents.PAY)\r\n           .guard(ctx -&gt; {\r\n               log.info(\u201c<span class=\"hljs-literal\">true<\/span>-&gt;statechanged. <span class=\"hljs-literal\">false<\/span>-&gt;<span class=\"hljs-keyword\">do<\/span> not change \u201d);\r\n               <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">paymentType<\/span> <span class=\"hljs-operator\">=<\/span> String.class.cast(ctx.getExtendedState()\r\n                       .getVariables().get(<span class=\"hljs-string\">\"paymentType\"<\/span>));\r\n               <span class=\"hljs-keyword\">if<\/span> (!StringUtils.isEmpty(paymentType) &amp;&amp; paymentType.equals(<span class=\"hljs-string\">\"cod\"<\/span>))\r\n                   <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">false<\/span>;\r\n               <span class=\"hljs-keyword\">else<\/span> <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">true<\/span>;\r\n           })\r\n\r\n           .and()\r\n           .withExternal()\r\n           .source(OrderStates.PAID)\r\n           .target(OrderStates.FULFILLED)\r\n           .event(OrderEvents.FULFILL)\r\n           .action(ctx -&gt; {\r\n             log.info(<span class=\"hljs-string\">\"This PAID handler where we can perform some logging\"<\/span>);\r\n           })\r\n\r\n           .and()\r\n           .withExternal()\r\n           .source(OrderStates.SUBMITTED)\r\n           .target(OrderStates.CANCELLED)\r\n           .event(OrderEvents.CANCEL)\r\n           .action(ctx -&gt; {\r\n             log.info(<span class=\"hljs-string\">\"This SUBMITTED handler where we can perform some logging\"<\/span>);\r\n           })\r\n\r\n\r\n           .and()\r\n           .withExternal()\r\n           .source(OrderStates.PAID)\r\n           .target(OrderStates.CANCELLED)\r\n           .event(OrderEvents.CANCEL)\r\n           .action(ctx -&gt; {\r\n               log.info(<span class=\"hljs-string\">\"This PAID handler where we can perform some logging\"<\/span>);\r\n           });\r\n\r\n }\r\n<\/span><\/pre>\n<p id=\"1774\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Here we define first the source state, from source we define the destination state when a particular event is triggered. Along with source, target and event we have also defined\u00a0<strong class=\"ih jd\">Guards<\/strong>\u00a0and\u00a0<strong class=\"ih jd\">Actions<\/strong>.<\/p>\n<p id=\"f6b0\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Here, we can see that for state\u00a0<strong class=\"ih jd\">FULFILLED<\/strong>,\u00a0<strong class=\"ih jd\">CANCELLED<\/strong>,\u00a0<strong class=\"ih jd\">PAID<\/strong>\u00a0we have used an\u00a0<strong class=\"ih jd\">Action<\/strong>\u00a0which can be used for cross cutting concerns like logging.<\/p>\n<p id=\"3f74\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Also, we have used\u00a0<strong class=\"ih jd\">Guard<\/strong>, which can be used to make a decision whether we want to block the state from being changed. If true, the state will change, otherwise it will not.<\/p>\n<p id=\"3c0f\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><strong class=\"ih jd\">Guard<\/strong><\/p>\n<p id=\"5eee\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">We can define a guard as a separate bean and then call it from guard directly.<\/p>\n<p id=\"d3ba\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Guard is a functional interface and hence we have used lambda for its implementation. The implementing method needs to return true or false. True means the state will change and false means the state will not change.<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"bda6\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\"> @Bean<\/span>\r\n<span class=\"hljs-keyword\"> public<\/span> Guard&lt;OrderStates, OrderEvents&gt; guard() {\r\n   <span class=\"hljs-keyword\">return<\/span> ctx -&gt; <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">true<\/span>;\r\n }<\/span><\/pre>\n<p id=\"d4a9\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><strong class=\"ih jd\">Action<\/strong><\/p>\n<p id=\"e483\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">We can define action as a separate bean and then call it directly from action. Action is a functional interface and hence we have used lambda for its implementation. It\u2019s just taking a consumer.<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"e050\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\"> @Bean<\/span>\r\n<span class=\"hljs-keyword\"> public<\/span> Action&lt;OrderStates, OrderEvents&gt; <span class=\"hljs-title.function\">guard<\/span><span class=\"hljs-params\">()<\/span> {\r\n   <span class=\"hljs-keyword\">return<\/span> ctx -&gt; log.inf(<span class=\"hljs-string\">\"logging\"<\/span>);\r\n }<\/span><\/pre>\n<h2 id=\"f20c\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\"><strong class=\"ih jd\">Step 5<\/strong><\/h2>\n<p id=\"6adf\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Let\u2019s create a simple\u00a0<strong class=\"ih jd\">OrderInvoice<\/strong>\u00a0Entity for which we are going to change the state using our state machine.<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"5d75\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\"> @Entity<\/span>\r\n<span class=\"hljs-meta\"> @Getter<\/span>\r\n<span class=\"hljs-meta\"> @Setter<\/span>\r\n<span class=\"hljs-keyword\"> public<\/span> <span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title.class\">OrderInvoice<\/span> {\r\n\r\n   <span class=\"hljs-meta\">@Id<\/span>\r\n   <span class=\"hljs-meta\">@GeneratedValue<\/span>\r\n   <span class=\"hljs-keyword\">private<\/span>  Long id;\r\n   <span class=\"hljs-keyword\">private<\/span> LocalDate localDate;\r\n   <span class=\"hljs-keyword\">private<\/span> String state;\r\n\r\n   <span class=\"hljs-meta\">@Transient<\/span>\r\n   String event;\r\n\r\n   <span class=\"hljs-meta\">@Transient<\/span>\r\n   String paymentType;\r\n\r\n }<\/span><\/pre>\n<pre class=\"ki jq jr js hr jt ju gh\"><span id=\"f334\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-keyword\"> public<\/span> <span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title.class\">OrderRepository<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title.class\">JpaRepository<\/span>&lt;OrderInvoice,Long&gt; {}<\/span><\/pre>\n<h2 id=\"b67e\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\"><strong class=\"ih jd\">Step 6<\/strong><\/h2>\n<p id=\"55b2\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">We will create simple controller class to first create a OrderInvoice for which state will be in\u00a0<strong class=\"ih jd\">SUBMITTED<\/strong>\u00a0state<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"7fd3\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\"> @PostMapping(\"\/createOrder\")<\/span>\r\n<span class=\"hljs-keyword\"> public<\/span> OrderInvoice <span class=\"hljs-title.function\">createOrder<\/span><span class=\"hljs-params\">()<\/span>{\r\n   <span class=\"hljs-type\">OrderInvoice<\/span> <span class=\"hljs-variable\">order<\/span> <span class=\"hljs-operator\">=<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">OrderInvoice<\/span>();\r\n   order.setState(OrderStates.SUBMITTED.name());\r\n   order.setLocalDate(LocalDate.now());\r\n   <span class=\"hljs-keyword\">return<\/span> orderRepository.save(order);\r\n }<\/span><\/pre>\n<h2 id=\"dbce\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\"><strong class=\"ih jd\">Step 7<\/strong><\/h2>\n<p id=\"e12f\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Now we need to create another handler that will use state machine to change the state of this order<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"af9c\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\"> @RestController<\/span>\r\n<span class=\"hljs-meta\"> @RequiredArgsConstructor<\/span>\r\n<span class=\"hljs-keyword\"> public<\/span> <span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title.class\">WorkflowController<\/span> {\r\n\r\n\r\n   <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> OrderRepository orderRepository;\r\n\r\n   <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> StateMachineFactory&lt;OrderStates, OrderEvents&gt; stateMachineFactory;\r\n\r\n\r\n\r\n   <span class=\"hljs-meta\">@PutMapping(<span class=\"hljs-string\">\"\/change\"<\/span>)<\/span>\r\n   <span class=\"hljs-keyword\">public<\/span> String changeState(<span class=\"hljs-meta\">@RequestBody<\/span> OrderInvoice order){\r\n\r\n        <span class=\"hljs-comment\">\/\/making the machine in current state of the order<\/span>\r\n         StateMachine&lt;OrderStates, OrderEvents&gt; sm =    build(order);\r\n         sm.getExtendedState().getVariables().put(<span class=\"hljs-string\">\"paymentType\"<\/span>,order.getPaymentType());\r\n         sm.sendEvent(\r\n                 MessageBuilder.withPayload(OrderEvents.valueOf(order.getEvent()))\r\n                         .setHeader(<span class=\"hljs-string\">\"orderId\"<\/span>,order.getId())\r\n                         .setHeader(<span class=\"hljs-string\">\"state\"<\/span>,order.getState())\r\n                         .build()\r\n                 );\r\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"state changed\"<\/span>;\r\n    }\r\n }\r\n<\/span><\/pre>\n<p id=\"1c87\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Here we can see the build(order) method which is very important. This method aims to get the state machine from state machine factory and set it to a state in which our current order is in. Usually current state is pulled from the database.<\/p>\n<p id=\"076b\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><strong class=\"ih jd\">Spring MessageBuilder<\/strong>\u00a0is quite useful in sending events as it allows us to attach headers which we have used to send events. In the following steps we would be defining an interceptor where we will be using the header inputs.<\/p>\n<h2 id=\"e820\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\"><strong class=\"ih jd\">Step 8<\/strong><\/h2>\n<p class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\"><strong class=\"ih jd\">(Most important step)<\/strong><\/p>\n<p id=\"8837\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Pulling the state from the database and reset the machine to that state. This is the most important part and may look cumbersome at first glance.<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"136b\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-keyword\"> public<\/span> StateMachine&lt;OrderStates,OrderEvents&gt; <span class=\"hljs-title.function\">build<\/span><span class=\"hljs-params\">(<span class=\"hljs-keyword\">final<\/span> OrderInvoice orderDto)<\/span>{\r\n   <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">orderDb<\/span> <span class=\"hljs-operator\">=<\/span>  <span class=\"hljs-built_in\">this<\/span>.orderRepository.findById(orderDto.getId());\r\n   <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">stateMachine<\/span> <span class=\"hljs-operator\">=<\/span>  <span class=\"hljs-built_in\">this<\/span>.stateMachineFactory.getStateMachine(orderDto.getId().toString());\r\n   stateMachine.stop();\r\n   stateMachine.getStateMachineAccessor()\r\n      .doWithAllRegions(sma -&gt; {\r\n        sma.resetStateMachine(<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">DefaultStateMachineContext<\/span>&lt;&gt;(OrderStates.valueOf(orderDb.get().getState()), <span class=\"hljs-literal\">null<\/span>, <span class=\"hljs-literal\">null<\/span>, <span class=\"hljs-literal\">null<\/span>));\r\n     });\r\n    stateMachine.start();\r\n    <span class=\"hljs-keyword\">return<\/span> stateMachine;\r\n }<\/span><\/pre>\n<p id=\"e33c\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">As, we can see that we have pulled the state machine first from stateMachineFactory which we have injected already, then we have stopped it, then we have reset the machine to a state of OrderInvoice which we have pulled from the database.<\/p>\n<h2 id=\"84a2\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\"><strong class=\"ih jd\">Step 9<\/strong><\/h2>\n<p id=\"84d1\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Once we have reset the machine to existing state with help of db, we just have to send the event which we have received in the request.<\/p>\n<p id=\"b43e\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">But we still need to persist the order state in our db when the state machine has altered the state of the order. We may take help of some interceptors as shown below<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"f985\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"><span class=\"hljs-keyword\"> public<\/span> StateMachine&lt;OrderStates,OrderEvents&gt; <span class=\"hljs-title.function\">build<\/span><span class=\"hljs-params\">(<span class=\"hljs-keyword\">final<\/span> OrderInvoice orderDto)<\/span>{\r\n   <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">orderDb<\/span> <span class=\"hljs-operator\">=<\/span>  <span class=\"hljs-built_in\">this<\/span>.orderRepository.findById(orderDto.getId());\r\n   <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">stateMachine<\/span> <span class=\"hljs-operator\">=<\/span>  <span class=\"hljs-built_in\">this<\/span>.stateMachineFactory.getStateMachine(orderDto.getId().toString());\r\n   stateMachine.stop();\r\n   stateMachine.getStateMachineAccessor()\r\n           .doWithAllRegions(sma -&gt; {\r\n               sma.addStateMachineInterceptor(<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">StateMachineInterceptorAdapter<\/span>&lt;&gt;() {\r\n                   <span class=\"hljs-meta\">@Override<\/span>\r\n                   <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title.function\">preStateChange<\/span><span class=\"hljs-params\">(State&lt;OrderStates, OrderEvents&gt; state, Message&lt;OrderEvents&gt; message, Transition&lt;OrderStates, OrderEvents&gt; transition, StateMachine&lt;OrderStates, OrderEvents&gt; stateMachine, StateMachine&lt;OrderStates, OrderEvents&gt; rootStateMachine)<\/span> {\r\n                      <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">orderId<\/span> <span class=\"hljs-operator\">=<\/span> Long.class.cast(message.getHeaders().get(<span class=\"hljs-string\">\"orderId\"<\/span>));\r\n                      <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">order<\/span> <span class=\"hljs-operator\">=<\/span>  orderRepository.findById(orderId);\r\n                       <span class=\"hljs-keyword\">if<\/span>(order.isPresent()){\r\n                           order.get().setState(state.getId().name());\r\n                           orderRepository.save(order.get());\r\n                       }\r\n                   }\r\n               });\r\n               sma.resetStateMachine(<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">DefaultStateMachineContext<\/span>&lt;&gt;(OrderStates.valueOf(orderDb.get().getState()), <span class=\"hljs-literal\">null<\/span>, <span class=\"hljs-literal\">null<\/span>, <span class=\"hljs-literal\">null<\/span>));\r\n           });\r\n\r\n    stateMachine.start();\r\n    <span class=\"hljs-keyword\">return<\/span> stateMachine;\r\n\r\n }<\/span><\/pre>\n<p id=\"0ef0\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Here we have added the\u00a0<strong class=\"ih jd\">addStateMAchineInterceptorAdaptor<\/strong> and override <strong class=\"ih jd\">preStateChange<\/strong>\u00a0method.<\/p>\n<p id=\"a6e9\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">It\u2019s time to test our application through curl commands<\/p>\n<p id=\"40fc\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><strong class=\"ih jd\">Test Step 1:<\/strong><\/p>\n<p id=\"f0ca\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">First we simply create the order. This will persist the order in SUBMITTED state<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"5ac6\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\">curl --location --request POST <span class=\"hljs-string\">'localhost:8080\/createOrder'<\/span> \\\r\n--<span class=\"hljs-keyword\">data<\/span>-raw <span class=\"hljs-string\">''<\/span><\/span><\/pre>\n<p id=\"59ba\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><strong class=\"ih jd\">Test Step 2:<\/strong><\/p>\n<p id=\"743f\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">We simply send an event of PAY for order id 1<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"4f23\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"> curl <span class=\"hljs-comment\">--location --request PUT 'localhost:8080\/change' \\<\/span>\r\n<span class=\"hljs-comment\"> --header 'Content-Type: application\/json' \\<\/span>\r\n<span class=\"hljs-comment\"> --data-raw '{<\/span>\r\n     <span class=\"hljs-string\">\"id\"<\/span>: <span class=\"hljs-number\">1<\/span>,\r\n     <span class=\"hljs-string\">\"event\"<\/span>: <span class=\"hljs-string\">\"PAY\"<\/span>,\r\n     <span class=\"hljs-string\">\"paymentType\"<\/span> : <span class=\"hljs-string\">\"cash\"<\/span>\r\n }<span class=\"hljs-string\">'<\/span><\/span><\/pre>\n<p id=\"5df2\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">The above curl will change the state to PAID as payment type is cash<\/p>\n<p id=\"89b0\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">But if the curl is as below<\/p>\n<pre class=\"jf jg jh ji fy jq jr js hr jt ju gh\"><span id=\"7db4\" class=\"jv jw ig jr b bn jx jy l jz ka\" data-selectable-paragraph=\"\"> curl --location --request PUT <span class=\"hljs-symbol\">'localhost<\/span>:<span class=\"hljs-number\">8080<\/span>\/change' \\\r\n --header <span class=\"hljs-symbol\">'Content<\/span>-Type: application\/json' \\\r\n --data-raw '{\r\n     <span class=\"hljs-string\">\"id\"<\/span>: <span class=\"hljs-number\">1<\/span>,\r\n     <span class=\"hljs-string\">\"event\"<\/span>: <span class=\"hljs-string\">\"PAY\"<\/span>,\r\n     <span class=\"hljs-string\">\"paymentType\"<\/span> : <span class=\"hljs-string\">\"cod\"<\/span>\r\n }'\r\n<\/span><\/pre>\n<p id=\"47d5\" class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\">Since we have a guard which will not allow to change the state if the action payment type is \u201c<strong class=\"ih jd\">cod<\/strong>\u201d.<\/p>\n<p class=\"pw-post-body-paragraph ie if ig ih b ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz ja jb jc hz gh\" data-selectable-paragraph=\"\"><span style=\"font-size: 1rem;\">This blog is originally published <\/span><a style=\"font-size: 1rem;\" href=\"https:\/\/medium.com\/@anil.gola\/state-machine-overview-75118e494059\">here<\/a><span style=\"font-size: 1rem;\">.<\/span><\/p>\n<div class=\"ap-custom-wrapper\"><\/div><!--ap-custom-wrapper-->","protected":false},"excerpt":{"rendered":"<p>State Machine Overview Spring state machine is a spring-module that allows us to describe well known, well understood transitions from one state to another. You have probably built a million state machines in your life if you have ever done any kind of programming. For example any kind of if-then-else statement at some point will [&hellip;]<\/p>\n","protected":false},"author":1520,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":629},"categories":[446],"tags":[5053],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/55965"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1520"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=55965"}],"version-history":[{"count":5,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/55965\/revisions"}],"predecessor-version":[{"id":58431,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/55965\/revisions\/58431"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=55965"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=55965"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=55965"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}