Search code examples
spring-mvcwebsocketstompspring-websocketsockjs

How to properly configure Stomp and SockJS endpoint in Spring MVC?


This is/may be duplicate of:
Websocket - InvalidStateError: The connection has not been established yet.

I am implementing Notification System. And want to initialize Socket connection when user Logged In, and show him his notifications, and also if some event happens.

My Code snippet as follows.

websocket.js :

var stompClient = null;
function connect( temp ) {
    alert(temp);
    //var socket = new SockJS("/websock");
    //var socket = new SockJS("/websock"+temp);
    var socket = new SockJS(context_path+"/websock"+temp);
    //context_path == "/SupportCenter"
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function( frame ){
        console.log( "Connected :- "+frame );
        stompClient.subscribe("/topic/notifications", function( notifications ) {
            alert( notifications );
        });
    }, function( error ) {
        alert( error );
    });
    alert();
    getNotifications();
}

function getNotifications() {
    stompClient.send("/app/hello", {}, "Hiiiiii");
}

WebSocketConfig.java :

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    /* (non-Javadoc)
     * @see org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer#registerStompEndpoints(org.springframework.web.socket.config.annotation.StompEndpointRegistry)
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/websock").withSockJS();
    }
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        // TODO Auto-generated method stub
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
}

WebSocketController.java :

@Controller
public class WebSocketController {

    @MessageMapping(value="/hello")
    @SendTo("/topic/notifications")
    public Notify hello() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Notify notify = new Notify();
        notify.setMessage("Hello World !!!");
        return notify;
    }
}

Some code Hom.jsp :

<script type="text/javascript" src="<c:url value="/resources/js/sockjs.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/js/stomp.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/js/websocket.js"/>"></script>


<script type="text/javascript">
$(document).ready(function() {
    //...

    connect( '${nsec}');
});

Why Firefox Console giving XML Parsing Error: no root element found Location: while in Network tab status code is 200 OK.

Console Tab enter image description here

Network Tab

enter image description here


Solution

  • Originaly posted to this question.

    This is because stompClient.connect() method is asynchronous. I doesn't pause the execution waiting until connection is established. When you call getNotifications() right after alert() most probably connection is not established yet (it might be established if alert() takes enough time to connect).

    You are supposed to call getNotifications() in stompClient.connect() callback (just like you do with stompClient.subscribe()) to be sure that connection is established by the time it gets invoked.

    For example:

    stompClient.connect({}, function( frame ){
        console.log( "Connected :- "+frame );
        stompClient.subscribe("/topic/notifications", function( notifications ) {
            alert( notifications );
        });
        getNotifications();
    }, function( error ) {
        alert( error );
    });
    

    Besides of that you may consider using @SubscribeMapping annotation in your java code to get rid of explicit message from JavaScript to get initial message from the server. This way the server sends initial message as soon as subscription is established.

    For example:

    @MessageMapping(value="/hello")
    @SubscribeMapping("/notifications")
    @SendTo("/topic/notifications")
    public Notify hello() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Notify notify = new Notify();
        notify.setMessage("Hello World !!!");
        return notify;
    }
    

    Then client code would look like following:

    stompClient.connect({}, function( frame ){
        console.log( "Connected :- "+frame );
        stompClient.subscribe("/topic/notifications", function( notifications ) {
            alert( notifications );
        });
    }, function( error ) {
        alert( error );
    });