Spring WebSocketStompClient: как десериализовать полезную нагрузку сообщения STOMP в универсальный тип - PullRequest
0 голосов
/ 31 марта 2019

В поисках лучшего способа десериализации полезной нагрузки сообщений STOMP для универсальных типов.

У меня есть служба websocket, которая получает сущность, сохраняет ее и отправляет сохраненную сущность переноса сообщений всем подписанным клиентам.

В обычном формате JSON это должно выглядеть так:

Клиент отправляет объект:

{
  "name": "New Facility Name"
}

Сообщение от службы, которое я пытаюсь обработать в клиенте:

{
  "type": "ADD",
  "entity": {
    "id": 123,
    "name": "New Facility Name"
  }
}

Вот реализация Java, которая не работает так, как я ожидаю.

Entity - JavaBean Facility с полями id и name.

Сообщение.

public final class UpdateMessage<T> {

    private final Type type;
    private final T entity;

    @JsonCreator
    public UpdateMessage(@JsonProperty("type") Type type, @JsonProperty("entity") T entity) {
        this.type = type;
        this.entity = entity;
    }

    // getters

    public enum Type {
        ADD,
        DELETE,
        EDIT
    }
}

Test.

@RunWith(SpringRunner.class)
@SpringBootTest
public class WebsocketClientTest {

    private CompletableFuture<UpdateMessage<Facility>> messageCompletableFuture = new CompletableFuture<>();

    @Autowired
    private WebsocketClient client;

    @Before
    public void setUp() {
        client.connect();
    }

    @After
    public void setDown() {
        client.disconnect();
    }

    @Test
    public void trySpringResolvableType() throws InterruptedException, TimeoutException, ExecutionException {
        StompSession session = client.getSession();
        session.subscribe("/topic/facility/update", new StompFrameHandler() {
            @Override
            public Type getPayloadType(StompHeaders stompHeaders) {
                return ResolvableType.forClassWithGenerics(UpdateMessage.class, Facility.class).getType();
            }

            @Override
            public void handleFrame(StompHeaders stompHeaders, Object payload) {
                //noinspection unchecked
                messageCompletableFuture.complete((UpdateMessage<Facility>) payload);
            }
        });

        Facility facility = new Facility();
        facility.setName("New Facility Name");
        client.getSession().send("/app/facility/post", facility);

        UpdateMessage<Facility> message = messageCompletableFuture.get(9, SECONDS);

        /* Get 'java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to Facility' here: */
        assertEquals(facility.getName(), message.getEntity().getName());
    }
}

Результаты с 'java.lang.ClassCastException: java.util.LinkedHashMap не может быть приведен к Facility'.

Пробовал также Джексона TypeReference<T>

@Override
public Type getPayloadType(StompHeaders stompHeaders) {
    return new TypeReference<UpdateMessage<Facility>>() {}.getType();
}

с тем же результатом и TypeFactory

@Override
public Type getPayloadType(StompHeaders stompHeaders) {
    return TypeFactory.defaultInstance().constructParametricType(UpdateMessage.class, Facility.class);
}

который бросает

org.springframework.messaging.converter.MessageConversionException: Unresolvable payload type [[simple type, class UpdateMessage<Facility>]] from handler type [class FacilityController$2]

от

org.springframework.messaging.simp.stomp.DefaultStompSession.invokeHandler(StompFrameHandler, Message<byte[]>, StompHeaders)

Мне удалось заставить его работать, используя класс-оболочку

public final class FacilityUpdateMessage {

    private final UpdateMessage<Facility> message;

    @JsonCreator
    public FacilityUpdateMessage(@JsonProperty("type") UpdateMessage.Type type,
                                 @JsonProperty("entity") Facility entity
    ) {
        message = new UpdateMessage<>(type, entity);
    }

    public UpdateMessage<Facility> getMessage() {
        return message;
    }
}
@Override
public Type getPayloadType(StompHeaders stompHeaders) {
    return FacilityUpdateMessage.class;
}

но я не уверен, что это лучшее решение.

...