initial
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
package com.example.statemachinedemo;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.config.EnableStateMachineFactory;
|
||||
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
|
||||
import org.springframework.statemachine.state.PseudoStateKind;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Collection;
|
||||
|
||||
@SpringBootApplication
|
||||
public class StatemachinedemoApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(StatemachinedemoApplication.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
enum OrderEvents {
|
||||
FULFILL, PAY, CANCEL
|
||||
}
|
||||
|
||||
enum OrderStates {
|
||||
SUBMITTED, PAID, FULFILLED, CANCELED
|
||||
// ,PAID2, PAID3
|
||||
}
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
class Runner implements ApplicationRunner {
|
||||
private final StateMachineFactory<OrderStates, OrderEvents> factory;
|
||||
//
|
||||
// @Override
|
||||
// public void run(ApplicationArguments args) throws Exception {
|
||||
// StateMachine<OrderStates, OrderEvents> stateMachine = factory.getStateMachine("order");
|
||||
// stateMachine.start();
|
||||
// log.info("Current state is {}", stateMachine.getState().getId());
|
||||
//
|
||||
// }
|
||||
private final Config1 c;
|
||||
private final Config2 c2;
|
||||
private final Config3 c3;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
c.a();
|
||||
c2.a();
|
||||
// c3.exportToSCXML();
|
||||
}
|
||||
}
|
||||
|
||||
class StateMachineDotExporter {
|
||||
public static <S, E> void export(StateMachine<S, E> stateMachine, PrintWriter writer) {
|
||||
writer.println("digraph stateMachine {");
|
||||
|
||||
for (State<S, E> state : stateMachine.getStates()) {
|
||||
writer.printf(" %s;%n", state.getId());
|
||||
}
|
||||
|
||||
for (Transition<S, E> transition : stateMachine.getTransitions()) {
|
||||
if (transition.getSource() != null && transition.getTarget() != null) {
|
||||
writer.printf(" %s -> %s [label=\"%s\"];%n",
|
||||
transition.getSource().getId(),
|
||||
transition.getTarget().getId(),
|
||||
transition.getTrigger() != null ? transition.getTrigger().getEvent() : "");
|
||||
}
|
||||
}
|
||||
|
||||
writer.println("}");
|
||||
}
|
||||
}
|
||||
|
||||
class StateMachinePlantUMLExporter {
|
||||
|
||||
public static <S, E> void export(StateMachine<S, E> stateMachine, PrintWriter writer) {
|
||||
writer.println("@startuml");
|
||||
writer.println("[*] --> " + stateMachine.getInitialState().getId());
|
||||
|
||||
for (Transition<S, E> transition : stateMachine.getTransitions()) {
|
||||
if (transition.getSource() != null && transition.getTarget() != null) {
|
||||
String source = transition.getSource().getId().toString();
|
||||
String target = transition.getTarget().getId().toString();
|
||||
String event = transition.getTrigger() != null && transition.getTrigger().getEvent() != null
|
||||
? transition.getTrigger().getEvent().toString()
|
||||
: "";
|
||||
writer.printf("%s --> %s : %s%n", source, target, event);
|
||||
}
|
||||
}
|
||||
|
||||
for (State<S, E> state : stateMachine.getStates()) {
|
||||
if (state.getPseudoState() != null &&
|
||||
state.getPseudoState().getKind() == PseudoStateKind.END) {
|
||||
writer.printf("%s --> [*]%n", state.getId());
|
||||
}
|
||||
}
|
||||
writer.println("@enduml");
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
class Config1 {
|
||||
private final StateMachineFactory<OrderStates, OrderEvents> factory;
|
||||
public void a() throws FileNotFoundException {
|
||||
StateMachine<OrderStates, OrderEvents> stateMachine = factory.getStateMachine("order");
|
||||
|
||||
StateMachineDotExporter exporter = new StateMachineDotExporter();
|
||||
try (PrintWriter out = new PrintWriter("statemachine.dot")) {
|
||||
StateMachineDotExporter.export(stateMachine, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
class Config2 {
|
||||
private final StateMachineFactory<OrderStates, OrderEvents> factory;
|
||||
public void a() throws FileNotFoundException {
|
||||
StateMachine<OrderStates, OrderEvents> stateMachine = factory.getStateMachine("order");
|
||||
|
||||
try (PrintWriter out = new PrintWriter("statemachine.uml.dot")) {
|
||||
StateMachinePlantUMLExporter.export(stateMachine, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
class SCXMLExporter {
|
||||
|
||||
// This method assumes you already have a state machine
|
||||
public <S, E> void exportToSCXML(StateMachine<S, E> stateMachine) throws Exception {
|
||||
// Create a document to represent the SCXML structure
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.newDocument();
|
||||
|
||||
// Root SCXML element
|
||||
Element scxmlElement = doc.createElement("scxml");
|
||||
scxmlElement.setAttribute("version", "1.0");
|
||||
doc.appendChild(scxmlElement);
|
||||
|
||||
// Define the states
|
||||
Element statesElement = doc.createElement("states");
|
||||
scxmlElement.appendChild(statesElement);
|
||||
|
||||
// Iterate over the states of the state machine
|
||||
Collection<State<S, E>> states = stateMachine.getStates();
|
||||
for (State<S, E> state : states) {
|
||||
Element stateElement = doc.createElement("state");
|
||||
stateElement.setAttribute("id", state.getId().toString());
|
||||
statesElement.appendChild(stateElement);
|
||||
}
|
||||
|
||||
// Define the transitions
|
||||
Element transitionsElement = doc.createElement("transitions");
|
||||
scxmlElement.appendChild(transitionsElement);
|
||||
|
||||
Collection<Transition<S, E>> transitions = stateMachine.getTransitions();
|
||||
for (Transition<S, E> transition : transitions) {
|
||||
Element transitionElement = doc.createElement("transition");
|
||||
transitionElement.setAttribute("from", transition.getSource().getId().toString());
|
||||
transitionElement.setAttribute("to", transition.getTarget().getId().toString());
|
||||
transitionElement.setAttribute("event", String.valueOf(transition.getTrigger()));
|
||||
transitionsElement.appendChild(transitionElement);
|
||||
}
|
||||
|
||||
// Write to file
|
||||
File file = new File("stateMachine.scxml");
|
||||
saveDocumentToFile(doc, file);
|
||||
}
|
||||
|
||||
private void saveDocumentToFile(Document doc, File file) throws Exception {
|
||||
// Serialize the document to a file (you can use your preferred XML writer)
|
||||
javax.xml.transform.Transformer transformer = javax.xml.transform.TransformerFactory.newInstance().newTransformer();
|
||||
javax.xml.transform.stream.StreamResult result = new javax.xml.transform.stream.StreamResult(file);
|
||||
javax.xml.transform.dom.DOMSource source = new javax.xml.transform.dom.DOMSource(doc);
|
||||
transformer.transform(source, result);
|
||||
}
|
||||
}
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
class Config3 {
|
||||
private final StateMachineFactory<OrderStates, OrderEvents> factory;
|
||||
public void exportToSCXML() throws Exception {
|
||||
StateMachine<OrderStates, OrderEvents> stateMachine = factory.getStateMachine("order");
|
||||
|
||||
SCXMLExporter exporter = new SCXMLExporter();
|
||||
File file = new File("statemachine.scxml");
|
||||
exporter.exportToSCXML(stateMachine);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachineFactory
|
||||
@Slf4j
|
||||
class SimpleEnumStateMachineConfiguration extends StateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal().source(OrderStates.SUBMITTED).target(OrderStates.PAID).event(OrderEvents.PAY)
|
||||
.and()
|
||||
.withExternal().source(OrderStates.PAID).target(OrderStates.FULFILLED).event(OrderEvents.FULFILL)
|
||||
.and()
|
||||
.withExternal().source(OrderStates.SUBMITTED).target(OrderStates.CANCELED).event(OrderEvents.CANCEL).guard(new Guard<OrderStates, OrderEvents>() {
|
||||
@Override
|
||||
public boolean evaluate(StateContext<OrderStates, OrderEvents> context) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.and()
|
||||
.withExternal().source(OrderStates.PAID).target(OrderStates.CANCELED).event(OrderEvents.CANCEL);
|
||||
|
||||
//
|
||||
// .and()
|
||||
// .withChoice()
|
||||
// .source(OrderStates.SUBMITTED)
|
||||
// .first(OrderStates.PAID2, new Guard<OrderStates, OrderEvents>() {
|
||||
// @Override
|
||||
// public boolean evaluate(StateContext<OrderStates, OrderEvents> context) {
|
||||
// return false;
|
||||
// }
|
||||
// })
|
||||
// .last(OrderStates.PAID3)
|
||||
// .and().withExternal().source(OrderStates.PAID2).target(OrderStates.CANCELED)
|
||||
// .and().withExternal().source(OrderStates.PAID3).target(OrderStates.CANCELED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states.withStates()
|
||||
.initial(OrderStates.SUBMITTED)
|
||||
.state(OrderStates.PAID)
|
||||
// .state(OrderStates.PAID2)
|
||||
// .state(OrderStates.PAID3)
|
||||
.end(OrderStates.FULFILLED)
|
||||
.end(OrderStates.CANCELED);
|
||||
|
||||
}
|
||||
@Override
|
||||
public void configure(StateMachineConfigurationConfigurer<OrderStates, OrderEvents> config) throws Exception {
|
||||
StateMachineListenerAdapter<OrderStates, OrderEvents> listenerAdapter = new StateMachineListenerAdapter<>() {
|
||||
@Override
|
||||
public void stateChanged(State<OrderStates, OrderEvents> from, State<OrderStates, OrderEvents> to) {
|
||||
log.info("state changed from {} to {}", from, to);
|
||||
}
|
||||
};
|
||||
config.withConfiguration()
|
||||
.autoStartup(false)
|
||||
.listener(listenerAdapter);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user