Usando la máquina de estado Spring en el ejemplo del protocolo ROSEU
El artículo describe el uso de la máquina de estado Spring usando un ejemplo de establecer una conexión de acuerdo con la tecnología ROSEU. Se establece una conexión entre dos operadores EDO en modo punto a punto o mediante un centro de itinerancia. Describe cómo administrar estados, cambiar estados por evento y realizar acciones específicas al cambiar de estado. Si está interesado, entonces pido un gato.

El protocolo ROSEU se describe en detalle
aquí .
Para un artículo, solo necesitamos conocer el principio de establecer una conexión entre dos clientes EDI. Cada uno de ellos envía una invitación para establecer una conexión. El tipo de invitación es "Solicitar" o "Interrumpir".
Para establecer una conexión, ambos participantes en el flujo de trabajo deben enviar una invitación del tipo "Solicitud". Después de eso, la conexión se considera establecida.
Si uno de los participantes envía una invitación de tipo "Interrupción", la conexión se desconecta.
En nuestro sistema, establecemos siete estados posibles para los participantes de EDI.
- Conexión fallida - NO_CONNECTION
- Nuestro cliente ha enviado una invitación a roaming. Pero aún no lo hemos entregado. Se justifica por la asincronía de la institución de la aplicación y su envío al centro de roaming. - INVITATION_SAVED
- Conexión establecida correctamente: ARE_CONNECTED
- Conexión terminada por iniciativa de uno de los participantes - CONNECTION_BROKEN
- Llegó una invitación externa, nuestro cliente no envió nada antes - INVITATION_RECEIVED
- Invitación de nuestro cliente aceptada por el centro de roaming - INVITATION_SEND
- Error de conexión: CONNECTION_ERROR
Posibles eventos:
- Nuestro cliente envió una invitación a "Solicitud". - OUTCOME_INVITATION
- Nuestro cliente envió una invitación "Break" - OUTCOME_REJECT
- Un cliente externo envió una invitación de "Solicitud" - INCOME_INVITATION
- El cliente externo envió una invitación de "interrupción" - INCOME_REJECT
- Roaming Center aceptó la invitación con éxito - RC_SUCCESS
- El centro de roaming no aceptó la invitación - RC_ERROR
Tabla de cambio de estado. La primera línea es el estado inicial. La primera columna es un evento. En la intersección: un nuevo estado.

Tal cambio de estado se puede codificar a través del interruptor, si, si no. Pero a través de la máquina de estado, en mi opinión, esto será más conveniente.
La lógica es la siguiente: si alguien se desconecta, cualquiera puede restablecerlo. Si se produjo un error al establecer la conexión, entonces no se puede hacer nada más, se requiere la corrección manual del problema.
La documentación detallada sobre la máquina de estado Spring fue tomada
de aquí .
Crearemos el auto a través del constructor
StateMachineBuilder.Builder<ClientsState, ContractorEvent> builder = StateMachineBuilder.builder();
A continuación, establecemos todos los estados posibles. El estado inicial se establece en el estado actual de los clientes.
builder.configureStates() .withStates() .initial(initialState) .states(EnumSet.allOf(ClientsState.class));
Configuramos el inicio automático del automóvil. De lo contrario, deberá iniciar manualmente
builder.configureConfiguration() .withConfiguration() .autoStartup(true);
A continuación, prescribimos tramos.
fuente : estado inicial,
destino : estado final,
evento : evento en el que se produce el cambio de estado,
acción : actualiza los estados del cliente.
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)
Después de crear la máquina de estado, pasamos el
evento a su entrada. Pero en
acción necesitamos información adicional para actualizar el estado de los clientes. Por lo tanto, ajustamos el
evento en el
mensaje y colocamos los datos necesarios en el
encabezado .
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();
Además en
acción, estos datos son extraídos y utilizados.
@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")); } }
También puede usar la
protección para evitar cambios de estado, pero en nuestro caso esto no es necesario.
Eso es básicamente todo. El código fuente para el ejemplo se puede tomar del
enlace .
En el artículo, examinamos cómo usar la máquina de estado Spring para codificar la tabla de transición de estado.
Estas no son todas sus capacidades, sino solo las más básicas. Espero que el artículo haya sido útil y lo aliente a usar este marco.
Si tiene experiencia personal al usarlo, bienvenido a comentar.
UPD
Durante el uso, se reveló una característica interesante: en caso de una excepción en una de las acciones predeterminadas, el error no se produce, sino que simplemente se registra. En este caso, la ejecución de toda la máquina de estado no se detiene. E incluso el método hasStateMachineError devuelve falso.
Como decisión, hicimos una AbstractAction en la que captamos la excepción y la colocamos en el contexto de la máquina de estado. Después de eso, en caso de una excepción, hasStateMachineError devuelve verdadero y luego lo procesamos más.
@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); } }