#java #stomp #spring-messaging
#java #stomp #spring-обмен сообщениями
Вопрос:
Ищете лучший способ десериализовать полезную нагрузку STOMP-сообщений до универсальных типов.
У меня есть служба websocket, которая получает объект, сохраняет его и отправляет сохраненный объект с переносом сообщений всем подписанным клиентам.
В обычном JSON это должно выглядеть так:
Клиент отправляет объект:
{
"name": "New Facility Name"
}
Сообщение от службы, которое я пытаюсь обработать в клиенте:
{
"type": "ADD",
"entity": {
"id": 123,
"name": "New Facility Name"
}
}
Вот реализация Java, которая работает не так, как я ожидаю.
Сущность — 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
}
}
Тест.
@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 не может быть приведен к объекту’.
Пробовал также Jackson’s 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;
}
но я не уверен, что это лучшее решение.