在ROSEU协议示例中使用Spring状态机
本文通过一个根据ROSEU技术建立连接的示例来描述Spring状态机的使用。 在两个EDO运营商之间以点对点模式或通过漫游中心建立了连接。 它描述了如何管理状态,按事件切换状态以及在更改状态时执行指定的操作。 如果有兴趣的话,我要一只猫。
此处详细描述了ROSEU协议。
对于一篇文章,我们只需要了解在两个EDI客户端之间建立连接的原理。 他们每个人都发送邀请建立连接。 邀请的类型为“请求”或“中断”。
为了建立连接,工作流程中的两个参与者都必须发送“请求”类型的邀请。 此后,将视为已建立连接。
如果参与者之一发送的邀请类型为“中断”,则连接断开。
在我们的系统中,我们为EDI参与者设置了七个可能的状态。
- 连接失败-NO_CONNECTION
- 我们的客户已发出漫游邀请。 但是我们还没有交付。 通过应用程序的机构及其发送到漫游中心的异步性可以证明这一点。 -INVITATION_SAVED
- 连接成功建立-ARE_CONNECTED
- 参与者之一主动断开连接-CONNECTION_BROKEN
- 收到外部邀请,我们的客户之前没有发送任何内容-INVITATION_RECEIVED
- 漫游中心接受了客户的邀请-INVITATION_SEND
- 连接错误-CONNECTION_ERROR
可能的事件:
- 我们的客户向“请求”发送了邀请。 -OUTCOME_INVITATION
- 我们的客户发送了邀请函“ Break”-OUTCOME_REJECT
- 外部客户发送了“请求”邀请-INCOME_INVITATION
- 外部客户发送了“中断”邀请-INCOME_REJECT
- 漫游中心已成功接受邀请-RC_SUCCESS
- 漫游中心未接受邀请-RC_ERROR
状态切换表。 第一行是初始状态。 第一列是一个事件。 在十字路口-一种新状态。

可以通过if-else开关对这种状态切换进行编码。 但是在我看来,通过状态机,这将更加方便。
逻辑如下-如果有人断开连接,则任何人都可以重新建立它。 如果建立连接时发生错误,则无法进行其他操作,需要手动纠正问题。
有关Spring状态机的详细文档
来自此处 。
我们将通过建造者制造汽车
StateMachineBuilder.Builder<ClientsState, ContractorEvent> builder = StateMachineBuilder.builder();
接下来,我们设置所有可能的状态。 初始状态设置为客户的当前状态。
builder.configureStates() .withStates() .initial(initialState) .states(EnumSet.allOf(ClientsState.class));
我们配置汽车的自动启动。 否则,您将必须手动启动
builder.configureConfiguration() .withConfiguration() .autoStartup(true);
接下来,我们规定付款。
源 -初始状态,
目标 -最终状态,
事件 -发生状态切换的事件,
操作 -更新客户端状态。
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)
创建状态机后,我们将
事件传递到其输入。 但是我们实际上需要其他信息来更新客户的状态。 因此,我们将
事件包装在
message中 ,并将必要的数据放在
header中 。
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();
进一步地,该数据被提取并使用。
@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")); } }
您也可以使用
防护来防止状态更改,但是在我们这种情况下,这不是必需的。
基本上就是这样。 该示例的源代码可以从
链接中获取 。
在本文中,我们研究了如何使用Spring状态机对状态转换表进行编码。
这不是其全部功能,而只是最基本的功能。 我希望本文对您有所帮助,并鼓励您使用此框架。
如果您有使用它的个人经验,请发表评论。
UPD
在使用过程中,发现了一个有趣的功能-在默认操作之一发生异常的情况下,不会引发错误,而只是记录错误。 在这种情况下,整个状态机的执行不会停止。 甚至hasStateMachineError方法也返回false。
作为决策,我们进行了一个AbstractAction,在其中捕获异常并将其置于状态机上下文中。 之后,在发生异常的情况下,hasStateMachineError返回true,然后我们对其进行进一步处理。
@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); } }