Use WebSockets with Spring, SockJS and Stomp

This guide walks you through the process of creating a "hello world" application that sends messages back and forth, between a browser and the server. WebSocket is a very thin, lightweight layer above TCP. It makes it very suitable to use "subprotocols" to embed messages. In this guide we’ll dive in and use STOMP messaging with Spring to create an interactive web application.

Maven Dependencies

First we need to add the Spring messaging modules in the POM file:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>4.1.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>4.1.6.RELEASE</version>
</dependency>

Spring MVC Configuration

Next, we need to add the message broker config to the Spring MVC config XML file.

<beans
    ...
    xmlns:websocket="http://www.springframework.org/schema/websocket"
    xsi:schemaLocation="
    ...
    http://www.springframework.org/schema/websocket
    http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">

    <websocket:message-broker
        application-destination-prefix="/app"
        user-destination-prefix="/user">
        <websocket:stomp-endpoint path="/websocket">
            <websocket:handshake-interceptors>
                <bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
            </websocket:handshake-interceptors>
            <websocket:sockjs session-cookie-needed="true" />
        </websocket:stomp-endpoint>
        <websocket:simple-broker prefix="/topic, /message" />
    </websocket:message-broker>

    <!-- Other MVC config omitted here-->
</beans>

The main thing here is the set up of the message broker for handling the message exchange between the server and its clients. This is done via the <message-broker> and its child tags. The tag <websocket:simple-broker>indicates we are using in-memory message broker.

The HttpSessionHandshakeInterceptor is used to get session in the WebSocket.  By default, the session will not bring to the WebSocket request.

It is easy to understand together with the server and client codes so I will include them below first before attempting to give a bit more explanations by cross-referencing the client and server codes.

Spring MVC Controller

@Controller
public class WebSocketController {
    @MessageMapping("/authorization.action")
    @SendToUser("/message/authorization")
    public Message authorizationAction(
            SimpMessageHeaderAccessor headerAccessor, Message message) {
        String csrfToken = message.getValue();
        Map<String, Object> sessionAttributes = headerAccessor.getSessionAttributes();
        Boolean isCsrfTokenValid = CsrfProtector.isCsrfTokenValid(csrfToken, sessionAttributes);

        return new Message("isCsrfTokenValid", isCsrfTokenValid.toString());
    }

    @SubscribeMapping("/getRealTimeJudgeResult.action/{submissionId}")
    public Message getRealTimeJudgeResultAction(
            @DestinationVariable long submissionId) {
        return new Message("Key", "Value # " + submissionId);
    }

    /* Inner Class for Messaging */
    private static class Message implements Serializable {
        public Message() { }

        public Message(String key, String value) {
            this.key = key;
            this.value = value;
        }

        public String getKey() {
            return key;
        }

        public String getValue() {
            return value;
        }

        private String key;

        private String value;

        private static final long serialVersionUID = -3430525797548136557L;
    }
}

Some explanation

  1. As you see, you can get the session of WebSocket in authorizationAction with SimpMessageHeaderAccessor.
  2. The @SendToUser annotation allows you send message to a specific subscriber. And you must setup user-destination-prefix in the configuration file.
  3. The @DestinationVariable annotation allows you to pass a parameter to a method, and you can return something relative to the subscriber.

Client sockJS and STOMP codes

var socket      = new SockJS('<c:url value="/websocket" />'),
    stompClient = Stomp.over(socket);

stompClient.connect({}, function(frame) {
    hasConntected = true;
    displayWebSocketConnectionStatus(true, hasConntected);

    stompClient.send('/app/authorization.action', {}, JSON.stringify({
        'key': 'csrfToken',
        'value': '${csrfToken}'
    }));

    stompClient.subscribe('/user/message/authorization', function(message){
        console.log(message);
    });

    stompClient.subscribe('/app/getRealTimeJudgeResult.action/${submissionId}', function(message){
        console.log(message);
    });
}, function() {
    return displayWebSocketConnectionStatus(false, hasConntected);
});

Reference

  • https://raymondhlee.wordpress.com/2014/01/19/using-spring-4-websocket-sockjs-and-stomp-support-to-implement-two-way-server-client-communication/
  • http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
  • https://spring.io/guides/gs/messaging-stomp-websocket/
Contact Us
  • Room 614, Zonghe Building, Harbin Institute of Technology
  • cshzxie [at] gmail.com