Partner – Microsoft – NPI (cat= Spring)
announcement - icon

Azure Spring Apps is a fully managed service from Microsoft (built in collaboration with VMware), focused on building and deploying Spring Boot applications on Azure Cloud without worrying about Kubernetes.

And, the Enterprise plan comes with some interesting features, such as commercial Spring runtime support, a 99.95% SLA and some deep discounts (up to 47%) when you are ready for production.

>> Learn more and deploy your first Spring Boot app to Azure.

You can also ask questions and leave feedback on the Azure Spring Apps GitHub page.

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.

Course – LS (cat=Spring)

Get started with Spring and Spring Boot, through the Learn Spring course:

>> THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.