Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending Error message in Spring websockets

I am trying to send error messages in Spring websockets with STOMP over SockJS.

I am basically trying to achieve which is being done here.

This is my Exception Handler

@MessageExceptionHandler
@SendToUser(value = "/queue/error",broadcast = false)
public ApplicationError handleException(Exception message) throws ApplicationError {
        return  new ApplicationError("test");
}

And I am subscribing to

stompClient.subscribe('/user/queue/error', stompErrorCallback, {token: accessToken});

User in my case is not authenticated, but from here

While user destinations generally imply an authenticated user, it isn’t required strictly. A WebSocket session that is not associated with an authenticated user can subscribe to a user destination. In such cases the @SendToUser annotation will behave exactly the same as with broadcast=false, i.e. targeting only the session that sent the message being handled.

All this works fine when I am throwing this error from myHandler which is my Websocket Handler defined in websocket config.

I have a ClientInboundChannelInterceptor which extends ChannelInterceptorAdapter which intercepts all the messages in preSend.

In case of any exception in this interceptor, I want to throw it back to the user session which sent this message,

public class ClientInboundChannelInterceptor extends ChannelInterceptorAdapter {
    @Autowired
    @Lazy(value = true)
    @Qualifier("brokerMessagingTemplate")
    private SimpMessagingTemplate simpMessagingTemplate;

    @Override
    public Message<?> preSend(Message message, MessageChannel channel) throws IllegalArgumentException{
         if(some thing goes wrong)
           throw new RuntimeException();
    }

    @MessageExceptionHandler
    @SendToUser(value = "/queue/error",broadcast = false)
    public ApplicationError handleException(RuntimeException message) throws    ApplicationError {
        return  new ApplicationError("test");
    }
}

@MessageExceptionHandler does not catch this exception. So I tried sending it to the user directly using simpMessagingTemplate.

I basically want to do :

simpMessagingTemplate.convertAndSendToUser(SOMETHING,"/queue/error",e);

SOMETHING should be the correct username but user is not authenticated in my case, so I can't use headerAccessor.getUser().getName()

I have even tried with

simpMessagingTemplate.convertAndSendToUser(headerAccessor.getHeader("","/queue/error",e, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, headerAccessor.getSessionId()));

but this is not working.

I have even tried headerAccessor.getSessionId() in the place of username, but that does not seem to work.

What is the correct way to do this?

What should I use as username in convertAndSendToUser?

like image 409
Karthik Avatar asked Nov 19 '15 14:11

Karthik


1 Answers

My initial intuition was correct, sessionId is used as the username in case of unauthenticated user situations, but the problem was with headers.

After few hours of debugging through @SendToUser and simpMessagingTemplate.convertAndSendToUser(), I realised that if we use @SendToUser headers will be set automatically and we have to explicitly define the headers if we are using simpMessagingTemplate.convertAndSendToUser().

@SendToUser was setting two headers,

simpMessageType:SimpMessageType.MESSAGE,simpSessionId:sessionId

So I have tried adding the headers,

String sessionId = headerAccessor.getSessionId();
Map<String,Object> headerMap = new HashMap<>();
headerMap.put("simpMessageType", SimpMessageType.MESSAGE);
headerMap.put("simpSessionId",sessionId);   
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(),"/queue/error",e,headerMap);

It did not work, I have tried giving the headers as MessageHeaders

String sessionId = headerAccessor.getSessionId();
Map<String,Object> headerMap = new HashMap<>();
headerMap.put("simpMessageType", SimpMessageType.MESSAGE);
headerMap.put("simpSessionId",sessionId); 
MessageHeaders headers = new MessageHeaders(headerMap);

simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(),"/queue/error",e,headers);

didn't work either.

After some more debugging I found out the correct way to set the headers, and probably this is the only way to create these headers(from SendToMethodReturnValueHandler.java).

private MessageHeaders createHeaders(String sessionId) {
    SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
    headerAccessor.setSessionId(sessionId);
    headerAccessor.setLeaveMutable(true);
    return headerAccessor.getMessageHeaders();
}

So finally,

String sessionId = headerAccessor.getSessionId();
template.convertAndSendToUser(sessionId,"/queue/error","tesssssts",createHeaders(sessionId));

did the trick.

like image 136
Karthik Avatar answered Nov 18 '22 18:11

Karthik