<

I just announced the new Spring 5 modules in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

In this quick tutorial, we’re going to illustrate how to send a message to a specific session or particular user using Spring WebSockets.

For an introduction to the above module, please refer to this article.

2. WebSocket Configuration

First of all, we need to configure our message broker and WebSocket application endpoint:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig
  extends AbstractWebSocketMessageBrokerConfigurer {
	
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic/", "/queue/");
	config.setApplicationDestinationPrefixes("/app");
    }
	 
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
	registry.addEndpoint("/greeting");
    }	
}

With @EnableWebSocketMessageBroker we enabled a broker-backed messaging over WebSocket using STOMP, which stands for Streaming Text Oriented Messaging Protocol. It’s important to remark that this annotation needs to be used in conjunction with the @Configuration

It isn’t mandatory to extend the AbstractWebSocketMessageBrokerConfigurer but, for the quick example, it’s easier to customize the imported configuration.

In the first method, we set up a simple memory-based message broker to carry the messages back to the client on destinations prefixed with “/topic” and “/queue”.

And, in the second, we registered stomp endpoints at “/greeting”.

In case that we want to enable SockJS, we have to amend the register part:

registry.addEndpoint("/greeting").withSockJS();

3. Get Session Id By Interceptor

One way to obtain the session id is adding a Spring Interceptor which will be trigger during the handshake and get the information from the request data.

This interceptor can be added directly in WebSocketConfig:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
        
registry
  .addEndpoint("/greeting")
  .setHandshakeHandler(new DefaultHandshakeHandler() {

      public boolean beforeHandshake(
        ServerHttpRequest request, 
        ServerHttpResponse response, 
        WebSocketHandler wsHandler,
        Map attributes) throws Exception {
 
            if (request instanceof ServletServerHttpRequest) {
                ServletServerHttpRequest servletRequest
                 = (ServletServerHttpRequest) request;
                HttpSession session = servletRequest
                  .getServletRequest().getSession();
                attributes.put("sessionId", session.getId());
            }
                return true;
        }}).withSockJS();
    }

4. WebSocket Endpoint

Starting with Spring 5.0.5.RELEASE, it isn’t necessary to do any customization because of the improvement of @SendToUser annotation, that allows us to send a message to a user destination via “/user/{sessionId}/…” rather than “/user/{user}/…“.

That means the annotation works relying on the session id of the input message, effectively sending a reply to destination private to the session:

@Controller
public class WebSocketController {

    @Autowired
    private SimpMessageSendingOperations messagingTemplate;

    private Gson gson = new Gson();
 
    @MessageMapping("/message")
    @SendToUser("/queue/reply")
    public String processMessageFromClient(
      @Payload String message, 
      Principal principal) throws Exception {
	return gson
          .fromJson(message, Map.class)
          .get("name").toString();
    }
	
    @MessageExceptionHandler
    @SendToUser("/queue/errors")
    public String handleException(Throwable exception) {
        return exception.getMessage();
    }
}

It’s import to remark that, @SendToUser indicates that the return value of a message-handling method should be sent as a Message to the specified destination(s) prepended with “/user/{username}.

5. WebSocket Client

function connect() {
    var socket = new WebSocket('ws://localhost:8080/greeting');
    ws = Stomp.over(socket);

    ws.connect({}, function(frame) {
        ws.subscribe("/user/queue/errors", function(message) {
            alert("Error " + message.body);
        });

        ws.subscribe("/user/queue/reply", function(message) {
            alert("Message " + message.body);
        });
    }, function(error) {
        alert("STOMP error " + error);
    });
}

function disconnect() {
    if (ws != null) {
        ws.close();
    }
    setConnected(false);
    console.log("Disconnected");
}

A new WebSocket is created pointing to “/greeting” for the mapping in WebSocketConfiguration.

When we subscribe the client to “/user/queue/errors” and “/user/queue/reply” is where we use the remarked information from the last section.

As we can see, @SendToUser points to “queue/errors” but the message will be sent to “/user/queue/errors“.

6. Conclusion

In this article, we’have explored a way to send a message directly to a user or session id with Spring WebSocket

As always, the full source code of the examples is available over on GitHub.

I just announced the new Spring 5 modules in REST With Spring:

>> CHECK OUT THE LESSONS

Leave a Reply

Be the First to Comment!

avatar
  Subscribe  
Notify of