Verwendung der Spring State Machine in einem praktischen Beispiel

Verwenden der Spring State Machine am Beispiel des ROSEU-Protokolls


Der Artikel beschreibt die Verwendung der Spring State Machine anhand eines Beispiels zum Herstellen einer Verbindung gemäß der ROSEU-Technologie. Eine Verbindung zwischen zwei EDO-Betreibern im Punkt-zu-Punkt-Modus oder über ein Roaming-Center wird hergestellt. Es wird beschrieben, wie Sie Status verwalten, Status nach Ereignis wechseln und bestimmte Aktionen ausführen, wenn Sie Status ändern. Bei Interesse bitte ich um eine Katze.

Bild

Das ROSEU-Protokoll wird hier ausführlich beschrieben.

Für einen Artikel müssen wir nur das Prinzip kennen, eine Verbindung zwischen zwei EDI-Clients herzustellen. Jeder von ihnen sendet eine Einladung zum Herstellen einer Verbindung. Die Art der Einladung ist entweder "Anfrage" oder "Pause".

Um eine Verbindung herzustellen, müssen beide Teilnehmer des Workflows eine Einladung vom Typ "Anfrage" senden. Danach gilt die Verbindung als hergestellt.

Wenn einer der Teilnehmer eine Einladung vom Typ "Pause" sendet, wird die Verbindung getrennt.

In unserem System legen wir sieben mögliche Status für EDI-Teilnehmer fest.

  1. Verbindung fehlgeschlagen - NO_CONNECTION
  2. Unser Kunde hat eine Einladung zum Roaming gesendet. Aber wir haben es noch nicht geliefert. Dies wird durch die Asynchronität der Einrichtung der Anwendung und deren Senden an das Roaming-Center gerechtfertigt. - INVITATION_SAVED
  3. Verbindung erfolgreich hergestellt - ARE_CONNECTED
  4. Die Verbindung wurde auf Initiative eines der Teilnehmer beendet - CONNECTION_BROKEN
  5. Eine externe Einladung kam, unser Kunde hat vorher nichts gesendet - INVITATION_RECEIVED
  6. Einladung unseres Kunden vom Roaming Center angenommen - INVITATION_SEND
  7. Verbindungsfehler - CONNECTION_ERROR

Mögliche Ereignisse:

  1. Unser Kunde hat eine Einladung zu "Request" gesendet. - OUTCOME_INVITATION
  2. Unser Kunde hat eine Einladung "Pause" gesendet - OUTCOME_REJECT
  3. Ein externer Client hat eine Einladung "Anfrage" gesendet - INCOME_INVITATION
  4. Der externe Client hat eine "Break" -Einladung gesendet - INCOME_REJECT
  5. Das Roaming Center hat die Einladung - RC_SUCCESS - erfolgreich angenommen
  6. Das Roaming Center hat die Einladung - RC_ERROR - nicht angenommen

Statusumschalttabelle. Die erste Zeile ist der Anfangsstatus. Die erste Spalte ist ein Ereignis. An der Kreuzung - ein neuer Status.

Bild

Eine solche Statusumschaltung kann über den Schalter codiert werden, wenn, wenn-sonst. Aber durch die Zustandsmaschine wird dies meiner Meinung nach bequemer sein.

Die Logik lautet wie folgt: Wenn jemand die Verbindung getrennt hat, kann jeder sie wiederherstellen. Wenn beim Herstellen der Verbindung ein Fehler aufgetreten ist, kann nichts mehr getan werden. Eine manuelle Behebung des Problems ist erforderlich.

Eine ausführliche Dokumentation zur Spring State Machine wurde von hier übernommen .

Wir werden das Auto durch den Erbauer erstellen

StateMachineBuilder.Builder<ClientsState, ContractorEvent> builder = StateMachineBuilder.builder(); 

Als nächstes setzen wir alle möglichen Status. Der Ausgangsstatus wird auf den aktuellen Status der Kunden gesetzt.

 builder.configureStates() .withStates() .initial(initialState) .states(EnumSet.allOf(ClientsState.class)); 

Wir konfigurieren den Autostart des Autos. Andernfalls müssen Sie manuell starten

 builder.configureConfiguration() .withConfiguration() .autoStartup(true); 

Als nächstes verschreiben wir Tranchen. Quelle - Anfangsstatus, Ziel - Endstatus, Ereignis - Ereignis, bei dem die Statusumschaltung erfolgt, Aktion - Aktualisierung des Clientstatus.

 builder.configureTransitions() .withExternal() .source(NO_CONNECTION) .target(INVITATION_RECEIVED) .event(INCOME_INVITATION) .action(updateStateAction) .and() .withExternal() .source(NO_CONNECTION) .target(CONNECTION_BROKEN) .event(INCOME_REJECT) .action(updateStateAction) 

Nach dem Erstellen der Zustandsmaschine übergeben wir das Ereignis an seine Eingabe. Wir benötigen jedoch zusätzliche Informationen, um den Status der Kunden zu aktualisieren. Daher verpacken wir das Ereignis in eine Nachricht und fügen die erforderlichen Daten in den Header ein .

 StateMachine<ClientsState, ContractorEvent> sm = builder.build(); Map<String, Object> clients = new HashMap<>(); clients.put("client1", "client11"); clients.put("client2", "client22"); MessageHeaders headers = new MessageHeaders(clients); Message<ContractorEvent> message = new GenericMessage<>(event, headers); sm.sendEvent(message); sm.stop(); 

Weiter in Aktion werden diese Daten extrahiert und verwendet.

 @Service public class UpdateStateAction implements Action<ClientsState, ContractorEvent> { @Override public void execute(StateContext<ClientsState, ContractorEvent> context) { System.out.println("Source state: " + context.getSource()); System.out.println("Target state: " + context.getTarget()); System.out.println("Event: " + context.getEvent()); MessageHeaders headers = context.getMessageHeaders(); System.out.println(headers.get("client1")); System.out.println(headers.get("client2")); } } 

Sie können Guard auch verwenden, um Statusänderungen zu verhindern. In unserem Fall ist dies jedoch nicht erforderlich.

Das ist es im Grunde. Der Quellcode für das Beispiel kann dem Link entnommen werden.

In diesem Artikel haben wir untersucht, wie die Spring-Zustandsmaschine zum Codieren der Zustandsübergangstabelle verwendet wird.

Dies sind nicht alle Funktionen, sondern nur die grundlegendsten. Ich hoffe, der Artikel war nützlich und ermutigt Sie, dieses Framework zu verwenden.

Wenn Sie persönliche Erfahrungen damit haben, können Sie dies gerne kommentieren.

UPD

Während der Verwendung wurde eine interessante Funktion entdeckt - im Falle einer Ausnahme in einer der Standardaktionen wird der Fehler nicht ausgelöst, sondern einfach protokolliert. In diesem Fall wird die Ausführung der gesamten Zustandsmaschine nicht gestoppt. Und selbst die hasStateMachineError-Methode gibt false zurück.
Als Entscheidung haben wir eine AbstractAction erstellt, in der wir die Ausnahme abfangen und in den Kontext der Zustandsmaschine stellen. Danach gibt hasStateMachineError im Falle einer Ausnahme true zurück und wird dann weiter verarbeitet.

 @Override public void execute(StateContext<StatesEnum, OperationsEnum> context) { try { prepareContext(context); executeInternal(context); } catch (Exception e) { logger.error("  state machine", e); context.getStateMachine().setStateMachineError(e); } } 

Source: https://habr.com/ru/post/de440788/


All Articles