I am implementing Websocket in spring-boot application.
Here is what my configuration file looks like:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue", "/topic");
registry.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/application").withSockJS();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(
new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (accessor != null) {
accessor.setUser(() -> accessor.getFirstNativeHeader("user"));
accessor.setLeaveMutable(true);
}
return message;
}
});
}
}
Here is how I am sending message to client:
simpMessagingTemplate.convertAndSendToUser(
String.valueOf(playerId),
"/queue/question",
fightQuestion.buildFightQuestionModel(playerId))
But sometime on the server side i am getting following exception :
org.springframework.messaging.MessageDeliveryException: Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is java.lang.IllegalStateException: Already immutable
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:146) ~[spring-messaging-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:122) ~[spring-messaging-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.messaging.StompSubProtocolHandler.afterSessionEnded(StompSubProtocolHandler.java:629) [spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.clearSession(SubProtocolWebSocketHandler.java:530) [spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.afterConnectionClosed(SubProtocolWebSocketHandler.java:399) [spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.afterConnectionClosed(WebSocketHandlerDecorator.java:85) ~[spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.afterConnectionClosed(LoggingWebSocketHandlerDecorator.java:72) ~[spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.afterConnectionClosed(ExceptionWebSocketHandlerDecorator.java:78) ~[spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession.close(AbstractSockJsSession.java:223) [spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.handler.WebSocketSessionDecorator.close(WebSocketSessionDecorator.java:160) [spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator.close(ConcurrentWebSocketSessionDecorator.java:252) [spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.messaging.StompSubProtocolHandler.sendToClient(StompSubProtocolHandler.java:490) [spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageToClient(StompSubProtocolHandler.java:460) [spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:366) [spring-websocket-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.messaging.support.ExecutorSubscribableChannel$SendTask.run(ExecutorSubscribableChannel.java:144) [spring-messaging-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_201]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_201]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]
Caused by: java.lang.IllegalStateException: Already immutable
at org.springframework.util.Assert.state(Assert.java:73) ~[spring-core-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.messaging.support.MessageHeaderAccessor$MutableMessageHeaders.getRawHeaders(MessageHeaderAccessor.java:641) ~[spring-messaging-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.messaging.support.MessageHeaderAccessor.setHeader(MessageHeaderAccessor.java:311) ~[spring-messaging-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.messaging.simp.SimpMessageHeaderAccessor.setUser(SimpMessageHeaderAccessor.java:178) ~[spring-messaging-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at com.application.config.WebSocketConfig$1.preSend(WebSocketConfig.java:39) ~[classes/:na]
at org.springframework.messaging.support.AbstractMessageChannel$ChannelInterceptorChain.applyPreSend(AbstractMessageChannel.java:178) ~[spring-messaging-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:132) ~[spring-messaging-5.2.3.RELEASE.jar:5.2.3.RELEASE]
... 17 common frames omitted
Comment From: rstoyanchev
Here is how I am sending message to client:
This has no impact whatsoever on processing of messages from the clientInboundChannel
.
To understand what happens there, you can debug to see what the interception chain for clientInboundChannel
looks like. You can break in your own intercertor or where the message is sent. Typically there should be an ImmutableMessageChannelInterceptor
at the end of the chain. As long as that's present the message is not marked immutable. There could also be another filter before yours.
I'll close this but feel free to comment with further findings, or if you suspect an issue.
Comment From: aroramayank2002
In ChannelInterceptor you should check the command, if its disconnect then setUser might cause issue so no need to execute that code.
` StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); StompCommand command = accessor.getCommand(); if(command.equals(StompCommand.CONNECT)){}
`