How does Spring WebSocket send message to a specific user?

smwikipedia picture smwikipedia · Nov 15, 2015 · Viewed 9.1k times · Source

I am reading the book Spring in Action 4 to work with STOMP messaging over WebSocket.

Suppose the user destination prefix is set as "/user" as below:

registry.setUserDestinationPrefix("/user");

Then client subscribes to a destination with below JavaScript code:

stomp.subscribe("/user/queue/notifications", handleNotifications);

Then on the server, the actual destination that the client subscribes to should be derived from its session, maybe like this:

/queue/notifications-user6hr83v6t  --- (1)

Then I use the SimpMessagingTemplate to send message to that user:

messaging.convertAndSendToUser( username, "/queue/notifications",
                           new Notification("You just got mentioned!"));

Then the message will be sent to destination like this:

/user/<username>/queue/notifications  ---(2)

Well, the two destinations (1) and (2) look different, how could the message ever reach the client?

Answer

Daniel Raczynski picture Daniel Raczynski · Feb 17, 2017

The path

/user/<username>/queue/notifications 

seems to be the "logical" path which is used in documentation. It is also initially created with convertAndSendToUser method. It is then translated into a technical format which is done in UserDestinationMessageHandler class in this line

UserDestinationResult result = this.destinationResolver.resolveDestination(message);

eg.

Given the subscription:

stompClient.subscribe('/user/queue/reply', function (greeting) { ...

sending a message with

stompClient.send("/app/personal", ...

and intercepting it with

@MessageMapping("/personal")
public void personalMessage(SimpMessageHeaderAccessor headerAccessor, PoCRequestMessage message) {

    SimpMessageHeaderAccessor ha = SimpMessageHeaderAccessor
            .create(SimpMessageType.MESSAGE);
    ha.setSessionId(headerAccessor.getSessionId());
    ha.setLeaveMutable(true);
    PoCReplyMessage reply = new PoCReplyMessage("Personal Message" + message.getName());
    simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(), "/queue/reply", reply, ha.getMessageHeaders());
}

the destination will be resolved as follows:

source destination: /user/zojdn53y/queue/reply
target destination: /queue/reply-userzojdn53y

this is how the final destination name is resolved. The target destination is the real name of the queue that is created (at least as long an external message broker is used - didn't check this for a simple in-memory broker but I assume this would be the same).

One important thing to note is that when you want to use an unauthenticated user (most often scenario when experimenting with Websockets) you need to additionally put the message headers in convertAndSendToUser method - this is well described in

Spring WebSocket @SendToSession: send message to specific session