From 05ef94e0eda50c73112a580bc01e39aa5dde2235 Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Sat, 14 May 2016 09:42:16 +0100 Subject: [PATCH] Add ordershipping sample - Fixes #225 --- settings.gradle | 1 + spring-statemachine-samples/build.gradle | 9 + .../java/demo/ordershipping/Application.java | 27 + .../ordershipping/StateMachineConfig.java | 132 +++ .../ordershipping/StateMachineController.java | 115 +++ .../StateMachineLogListener.java | 45 + .../src/main/resources/application.yml | 3 + .../src/main/resources/logback.xml | 8 + .../src/main/resources/ordershipping.di | 2 + .../src/main/resources/ordershipping.notation | 875 ++++++++++++++++++ .../src/main/resources/ordershipping.uml | 126 +++ .../src/main/resources/templates/states.html | 71 ++ .../ordershipping/OrdershippingTests.java | 93 ++ .../demo/ordershipping/StateMachineTests.java | 145 +++ 14 files changed, 1652 insertions(+) create mode 100644 spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/Application.java create mode 100644 spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineConfig.java create mode 100644 spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineController.java create mode 100644 spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineLogListener.java create mode 100644 spring-statemachine-samples/ordershipping/src/main/resources/application.yml create mode 100644 spring-statemachine-samples/ordershipping/src/main/resources/logback.xml create mode 100644 spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.di create mode 100644 spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.notation create mode 100644 spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.uml create mode 100644 spring-statemachine-samples/ordershipping/src/main/resources/templates/states.html create mode 100644 spring-statemachine-samples/ordershipping/src/test/java/demo/ordershipping/OrdershippingTests.java create mode 100644 spring-statemachine-samples/ordershipping/src/test/java/demo/ordershipping/StateMachineTests.java diff --git a/settings.gradle b/settings.gradle index e2b594a6..fe030488 100644 --- a/settings.gradle +++ b/settings.gradle @@ -23,6 +23,7 @@ include 'spring-statemachine-samples:scope' include 'spring-statemachine-samples:security' include 'spring-statemachine-samples:eventservice' include 'spring-statemachine-samples:deploy' +include 'spring-statemachine-samples:ordershipping' rootProject.children.find { if (it.name == 'spring-statemachine-recipes') { diff --git a/spring-statemachine-samples/build.gradle b/spring-statemachine-samples/build.gradle index 55402f76..c53b0579 100644 --- a/spring-statemachine-samples/build.gradle +++ b/spring-statemachine-samples/build.gradle @@ -100,3 +100,12 @@ project('spring-statemachine-samples-deploy') { compile("org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion") } } + +project('spring-statemachine-samples-ordershipping') { + description = 'Spring State Machine Order Shipping Sample' + dependencies { + compile project(":spring-statemachine-uml") + compile("org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion") + } +} + diff --git a/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/Application.java b/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/Application.java new file mode 100644 index 00000000..2e5abf68 --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/Application.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 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 demo.ordershipping; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineConfig.java b/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineConfig.java new file mode 100644 index 00000000..941f0874 --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineConfig.java @@ -0,0 +1,132 @@ +/* + * Copyright 2016 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 demo.ordershipping; + +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.EnableStateMachineFactory; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineModelConfigurer; +import org.springframework.statemachine.config.model.StateMachineModelFactory; +import org.springframework.statemachine.guard.Guard; +import org.springframework.statemachine.uml.UmlStateMachineModelFactory; +import org.springframework.util.StringUtils; + +@Configuration +public class StateMachineConfig { + + @Bean + public StateMachineLogListener stateMachineLogListener() { + return new StateMachineLogListener(); + } + + @Configuration + @EnableStateMachineFactory + public static class Config extends StateMachineConfigurerAdapter { + + @Autowired + private StateMachineLogListener stateMachineLogListener; + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(stateMachineLogListener); + } + + @Override + public void configure(StateMachineModelConfigurer model) + throws Exception { + model + .withModel() + .factory(modelFactory()); + } + + @Bean + public StateMachineModelFactory modelFactory() { + return new UmlStateMachineModelFactory("classpath:ordershipping.uml"); + } + + @Bean + public Action entryReceiveOrder() { + return (context) -> { + String customer = context.getMessageHeaders().get("customer", String.class); + if (StringUtils.hasText(customer)) { + context.getExtendedState().getVariables().put("customer", customer); + } + String order = context.getMessageHeaders().get("order", String.class); + if (StringUtils.hasText(order)) { + context.getExtendedState().getVariables().put("order", order); + } + }; + } + + @Bean + public Action entrySendReminder() { + return (context) -> { + System.out.println("REMIND"); + }; + } + + @Bean + public Action entryHandleOrder() { + return (context) -> { + if (context.getMessageHeaders().containsKey("makeProdPlan")) { + context.getExtendedState().getVariables().put("makeProdPlan", true); + } + if (context.getMessageHeaders().containsKey("produce")) { + context.getExtendedState().getVariables().put("produce", true); + } + }; + } + + @Bean + public Guard orderOk() { + return (context) -> { + Map variables = context.getExtendedState().getVariables(); + return variables.containsKey("customer") && variables.containsKey("order"); + }; + } + + @Bean + public Guard paymentOk() { + return (context) -> { + return context.getMessageHeaders().containsKey("payment"); + }; + } + + @Bean + public Guard makeProdPlan() { + return (context) -> { + return context.getExtendedState().getVariables().containsKey("makeProdPlan"); + }; + } + + @Bean + public Guard produce() { + return (context) -> { + return context.getExtendedState().getVariables().containsKey("produce"); + }; + } + } +} diff --git a/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineController.java b/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineController.java new file mode 100644 index 00000000..b9bf02ea --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineController.java @@ -0,0 +1,115 @@ +/* + * Copyright 2016 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 demo.ordershipping; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.statemachine.StateMachine; +import org.springframework.statemachine.config.StateMachineFactory; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +public class StateMachineController { + + private final static String[] CUSTOMERS = new String[] { "customer1", "customer2", "customer3" }; + private final static String[] ORDERS = new String[] { "order1", "order2", "order3" }; + private final static String[] EVENTS = new String[] { "PLACE_ORDER", "RECEIVE_PAYMENT" }; + private final static String[] GUIDES = new String[] { "makeProdPlan", "produce", "payment" }; + + @Autowired + private StateMachineFactory stateMachineFactory; + + @Autowired + private StateMachineLogListener listener; + + private final Map> machines = new HashMap<>(); + + @RequestMapping("/") + public String home() { + return "redirect:/state"; + } + + @RequestMapping("/state") + public String feedAndGetStates( + @RequestParam(value = "action", required = false) String action, + @RequestParam(value = "customer", required = false) String customer, + @RequestParam(value = "order", required = false) String order, + @RequestParam(value = "event", required = false) String event, + @RequestParam(value = "guide", required = false) List guides, + Model model) throws Exception { + StateMachine stateMachine = null; + if (StringUtils.hasText(customer) && StringUtils.hasText(order)) { + stateMachine = getMachine(customer + ":" + order); + } + + if (stateMachine != null && StringUtils.hasText(event) && ObjectUtils.nullSafeEquals(action, "event")) { + Map headers = new HashMap<>(); + if (ObjectUtils.nullSafeEquals(event, EVENTS[0])) { + headers.put("customer", customer); + headers.put("order", order); + } + if (guides != null) { + if (guides.contains("makeProdPlan")) { + headers.put("makeProdPlan", true); + } + if (guides.contains("produce")) { + headers.put("produce", true); + } + if (guides.contains("payment")) { + headers.put("payment", true); + } + } + stateMachine.sendEvent(MessageBuilder.createMessage(event, new MessageHeaders(headers))); + } + + model.addAttribute("allIds", machines.keySet()); + model.addAttribute("allCustomers", CUSTOMERS); + model.addAttribute("allOrders", ORDERS); + model.addAttribute("allTypes", EVENTS); + model.addAttribute("allGuides", GUIDES); + model.addAttribute("machines", machines.values()); + model.addAttribute("messages", createMessages(listener.getMessages())); + return "states"; + } + + private synchronized StateMachine getMachine(String id) { + StateMachine machine = machines.get(id); + if (machine == null) { + machine = stateMachineFactory.getStateMachine(id); + machines.put(id, machine); + } + return machine; + } + + private String createMessages(List messages) { + StringBuilder buf = new StringBuilder(); + for (String message : messages) { + buf.append(message); + buf.append("\n"); + } + return buf.toString(); + } +} diff --git a/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineLogListener.java b/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineLogListener.java new file mode 100644 index 00000000..ddeb985c --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/java/demo/ordershipping/StateMachineLogListener.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 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 demo.ordershipping; + +import java.util.LinkedList; +import java.util.List; + +import org.springframework.statemachine.StateContext; +import org.springframework.statemachine.StateContext.Stage; +import org.springframework.statemachine.listener.StateMachineListenerAdapter; + +public class StateMachineLogListener extends StateMachineListenerAdapter { + + private final LinkedList messages = new LinkedList(); + + public List getMessages() { + return messages; + } + + public void resetMessages() { + messages.clear(); + } + + @Override + public void stateContext(StateContext stateContext) { + if (stateContext.getStage() == Stage.STATE_ENTRY) { + messages.addFirst(stateContext.getStateMachine().getId() + " enter " + stateContext.getTarget().getId()); + } else if (stateContext.getStage() == Stage.STATE_EXIT) { + messages.addFirst(stateContext.getStateMachine().getId() + " exit " + stateContext.getSource().getId()); + } + } +} diff --git a/spring-statemachine-samples/ordershipping/src/main/resources/application.yml b/spring-statemachine-samples/ordershipping/src/main/resources/application.yml new file mode 100644 index 00000000..5aa882d0 --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/resources/application.yml @@ -0,0 +1,3 @@ +security: + basic: + enabled: false diff --git a/spring-statemachine-samples/ordershipping/src/main/resources/logback.xml b/spring-statemachine-samples/ordershipping/src/main/resources/logback.xml new file mode 100644 index 00000000..7d5b0732 --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/resources/logback.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.di b/spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.di new file mode 100644 index 00000000..bf9abab3 --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.di @@ -0,0 +1,2 @@ + + diff --git a/spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.notation b/spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.notation new file mode 100644 index 00000000..22affb11 --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.notation @@ -0,0 +1,875 @@ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.uml b/spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.uml new file mode 100644 index 00000000..e7198dac --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/resources/ordershipping.uml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + bean + orderOk + + + + + + + + + bean + entryReceiveOrder + + + + + + + + + + bean + entryHandleOrder + + + + + + + + + + + + + bean + paymentOk + + + + + + + + + + + bean + entrySendReminder + + + + + + + + + + + + + + + + + bean + makeProdPlan + + + + + + + bean + produce + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-statemachine-samples/ordershipping/src/main/resources/templates/states.html b/spring-statemachine-samples/ordershipping/src/main/resources/templates/states.html new file mode 100644 index 00000000..4cab203f --- /dev/null +++ b/spring-statemachine-samples/ordershipping/src/main/resources/templates/states.html @@ -0,0 +1,71 @@ + + + + Spring Statemachine Ordershipping Demo + + + +
+
+

+

+
+
    +
  • + + +
  • +
+
+
+

+

+
+
    +
  • + + +
  • +
+
+ +
+

+

+
    +
  • + + +
  • +
+
    +
  • + + +
  • +
+ +
+
+

+ + + + + + + + + +
IdStates
+

+
+
+ +
+
+
+