Usando a máquina de estado Spring em um exemplo prático

Usando a máquina de estado Spring no exemplo do protocolo ROSEU


O artigo descreve o uso da máquina de estado Spring usando um exemplo de estabelecimento de uma conexão de acordo com a tecnologia ROSEU. É estabelecida uma conexão entre dois operadores EDO no modo ponto a ponto ou através de um centro de roaming. Ele descreve como gerenciar estados, alternar estados por evento e executar ações especificadas ao alterar estados. Se estiver interessado, peço um gato.

imagem

O protocolo ROSEU é descrito em detalhes aqui .

Para um artigo, precisamos apenas conhecer o princípio de estabelecer uma conexão entre dois clientes EDI. Cada um deles envia um convite para estabelecer uma conexão. O tipo de convite é "Solicitação" ou "Interrupção".

Para estabelecer uma conexão, os dois participantes do fluxo de trabalho devem enviar um convite do tipo "Solicitação". Depois disso, a conexão é considerada estabelecida.

Se um dos participantes enviar um convite do tipo "Interromper", a conexão será desconectada.

Em nosso sistema, estabelecemos sete status possíveis para os participantes do EDI.

  1. Falha na conexão - NO_CONNECTION
  2. Nosso cliente enviou um convite para roaming. Mas ainda não o entregamos. É justificado pela assincronia da instituição do pedido e pelo seu envio ao centro de roaming. - INVITATION_SAVED
  3. Conexão estabelecida com sucesso - ARE_CONNECTED
  4. Conexão encerrada por iniciativa de um dos participantes - CONNECTION_BROKEN
  5. Recebeu um convite externo, nosso cliente não enviou nada antes - INVITATION_RECEIVED
  6. Convite do nosso cliente aceito pelo centro de roaming - INVITATION_SEND
  7. Erro de conexão - CONNECTION_ERROR

Eventos possíveis:

  1. Nosso cliente enviou um convite para "Solicitar". - OUTCOME_INVITATION
  2. Nosso cliente enviou um convite "Break" - OUTCOME_REJECT
  3. Um cliente externo enviou um convite de "Solicitação" - INCOME_INVITATION
  4. O cliente externo enviou um convite para "Intervalo" - INCOME_REJECT
  5. Roaming Center aceitou o convite com sucesso - RC_SUCCESS
  6. O centro de roaming não aceitou o convite - RC_ERROR

Tabela de mudança de status. A primeira linha é o status inicial. A primeira coluna é um evento. No cruzamento - um novo status.

imagem

Essa mudança de status pode ser codificada através da opção if-if-else. Mas através da máquina de estado, na minha opinião, isso será mais conveniente.

A lógica é a seguinte - se alguém for desconectado, qualquer pessoa poderá restabelecê-la. Se ocorreu um erro ao estabelecer uma conexão, nada mais pode ser feito; a solução de problemas manual é necessária.

A documentação detalhada sobre a máquina do estado Spring foi retirada daqui .

Vamos criar o carro através do construtor

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

Em seguida, definimos todos os status possíveis. O status inicial é definido como o status atual dos clientes.

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

Nós configuramos a inicialização automática do carro. Caso contrário, você terá que iniciar manualmente

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

Em seguida, prescrevemos tranches. origem - status inicial, destino - status final, evento - evento no qual a troca de status ocorre, ação - atualiza os status do 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) 

Depois de criar a máquina de estado, passamos o evento para sua entrada. Mas, em ação, precisamos de informações adicionais para atualizar o status dos clientes. Portanto, envolvemos o evento na mensagem e colocamos os dados necessários no cabeçalho .

 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(); 

Além disso , esses dados são extraídos e usados.

 @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")); } } 

Você também pode usar a proteção para impedir alterações de status, mas, no nosso caso, isso não é necessário.

É basicamente isso. O código fonte do exemplo pode ser obtido no link .

No artigo, examinamos como usar a máquina de estado Spring para codificar a tabela de transição de estado.

Este não é todos os seus recursos, mas apenas o mais básico. Espero que o artigo tenha sido útil e incentive você a usar essa estrutura.

Se você tem experiência pessoal em usá-lo, faça o comentário.

UPD

Durante o uso, um recurso interessante foi revelado - no caso de uma exceção em uma das ações padrão, o erro não é gerado, mas simplesmente registrado. Nesse caso, a execução de toda a máquina de estado não para. E mesmo o método hasStateMachineError retorna false.
Como decisão, fizemos uma AbstractAction na qual capturamos a exceção e a colocamos no contexto da máquina de estado. Depois disso, no caso de uma exceção, hasStateMachineError retorna true e, em seguida, processamos mais.

 @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/pt440788/


All Articles