I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

In this article, we’ll create a simple web application that implements messaging using the new WebSocket capabilities introduced with Spring Framework 4.0.

WebSockets is a bi-directional, full-duplex, persistent connection between a web browser and a server. Once a WebSocket connection is established the connection stays open until the client or server decides to close this connection.

A typical use case could be when an app involves multiple users communicating with each other, like in a chat. We will build a simple chat client in our example.

2. Maven Dependencies

Since this is a Maven-based project, we first add the required dependencies to the pom.xml:

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

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

In addition, as we’ll use JSON to build the body of our messages, we need to add the Jackson dependencies. This allows Spring to convert our Java object to/from JSON:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.7.3</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId> 
    <version>2.7.3</version>
</dependency>

If you want to get the newest version of the libraries above, look for them on Maven Central.

3. Enable WebSocket in Spring

The first thing to do is to enable the WebSocket capabilities. To do this we need to add a configuration to our application and annotate this class with @EnableWebSocketMessageBroker.

As its name suggests, it enables WebSocket message handling, backed by a message broker:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
         registry.addEndpoint("/chat");
         registry.addEndpoint("/chat").withSockJS();
    }
}

Here, we can see that the method configureMessageBroker is used to configure the message broker. First, we enable an in-memory message broker to carry the messages back to the client on destinations prefixed with “/topic”.

We complete our simple configuration by designating the “/app” prefix to filter destinations targeting application annotated methods (via @MessageMapping).

The registerStompEndpoints method registers the “/chat” endpoint, enabling Spring’s STOMP support. Keep in mind that we are also adding here an endpoint that works without the SockJS for the sake of elasticity.

This endpoint, when prefixed with “/app”, is the endpoint that the ChatController.send() method is mapped to handle.

It also enables the SockJS fallback options, so that alternative messaging options may be used if WebSockets are not available. This is useful since WebSocket is not supported in all browsers yet and may be precluded by restrictive network proxies.

The fallbacks let the applications use a WebSocket API but gracefully degrade to non-WebSocket alternatives when necessary at runtime.

4. Create the Message Model

Now that we’ve set up the project and configured the WebSocket capabilities, we need to create a message to send.

The endpoint will accept messages containing the sender name and a text in a STOMP message whose body is a JSON object.

The message might look like this:

{
    "from": "John",
    "text": "Hello!"
}

To model the message carrying the text, we can create a simple Java object with from and text properties:

public class Message {

    private String from;
    private String text;

    // getters and setters
}

By default, Spring will use the Jackson library to convert our model object to and from JSON.

5. Create a Message-Handling Controller

As we’ve seen, Spring’s approach to working with STOMP messaging is to associate a controller method to the configured endpoint. This is made possible through the @MessageMapping annotation.

The association between the endpoint and the controller gives us the ability to handle the message if needed:

@MessageMapping("/chat")
@SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
    String time = new SimpleDateFormat("HH:mm").format(new Date());
    return new OutputMessage(message.getFrom(), message.getText(), time);
}

For the purposes of our example, we’ll create another model object named OutputMessage to represent the output message sent to the configured destination. We populate our object with the sender and the message text taken from the incoming message and enrich it with a timestamp.

After handling our message, we send it to the appropriate destination defined with the @SendTo annotation. All subscribers to the “/topic/messages” destination will receive the message.

6. Create a Browser Client

After making our configurations in the server-side, we’ll use the sockjs-client library to build a simple HTML page that interacts with our messaging system.

First of all, we need to import the sockjs and stomp Javascript client libraries. Next, we can create a connect() function to open the communication with our endpoint, a sendMessage() function to send our STOMP message and a disconnect() function to close the communication:

<html>
    <head>
        <title>Chat WebSocket</title>
        <script src="./js/sockjs-0.3.4.js"></script>
        <script src="./js/stomp.js"></script>
        <script type="text/javascript">
            var stompClient = null;
            
            function setConnected(connected) {
                document.getElementById('connect').disabled = connected;
                document.getElementById('disconnect').disabled = !connected;
                document.getElementById('conversationDiv').style.visibility 
                  = connected ? 'visible' : 'hidden';
                document.getElementById('response').innerHTML = '';
            }
            
            function connect() {
                var socket = new SockJS('/spring-mvc-java/chat');
                stompClient = Stomp.over(socket);  
                stompClient.connect({}, function(frame) {
                    setConnected(true);
                    console.log('Connected: ' + frame);
                    stompClient.subscribe('/topic/messages', function(messageOutput) {
                        showMessageOutput(JSON.parse(messageOutput.body));
                    });
                });
            }
            
            function disconnect() {
                if(stompClient != null) {
                    stompClient.disconnect();
                }
                setConnected(false);
                console.log("Disconnected");
            }
            
            function sendMessage() {
                var from = document.getElementById('from').value;
                var text = document.getElementById('text').value;
                stompClient.send("/app/chat", {}, 
                  JSON.stringify({'from':from, 'text':text}));
            }
            
            function showMessageOutput(messageOutput) {
                var response = document.getElementById('response');
                var p = document.createElement('p');
                p.style.wordWrap = 'break-word';
                p.appendChild(document.createTextNode(messageOutput.from + ": " 
                  + messageOutput.text + " (" + messageOutput.time + ")"));
                response.appendChild(p);
            }
        </script>
    </head>
    <body onload="disconnect()">
        <div>
            <div>
                <input type="text" id="from" placeholder="Choose a nickname"/>
            </div>
            <br />
            <div>
                <button id="connect" onclick="connect();">Connect</button>
                <button id="disconnect" disabled="disabled" onclick="disconnect();">
                    Disconnect
                </button>
            </div>
            <br />
            <div id="conversationDiv">
                <input type="text" id="text" placeholder="Write a message..."/>
                <button id="sendMessage" onclick="sendMessage();">Send</button>
                <p id="response"></p>
            </div>
        </div>

    </body>
</html>

7. Testing the Example

To test our example, we can open a couple of browser windows and access the chat page at:

http://localhost:8080/spring-mvc-java/resources/chat.html

Once this is done, we can join the chat by entering a nickname and hitting the connect button. If we compose and send a message we can see it in all browser sessions that have joined the chat.

Take a look at the screenshot to see an example:

screenshot

8. Conclusion

In this tutorial, we’ve explored Spring’s WebSocket support. We’ve seen its server-side configuration and built a simple client-side counterpart with the use of sockjs and stomp Javascript libraries.

The example code can be found in the GitHub project.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS

newest oldest most voted
Notify of
Lisdey Pérez
Guest
Lisdey Pérez

Hello and thanks for so helpfull post… I dont know is this is the right place to do this, but I need some help, I need to create a Java client that is able to connect to a WebSocket server like the one created in this post… using Spring + Stomp + Sockjs like this post… I mean instead the JS client a Java client. I have tried all the post that I could find about it… but it still dont work, can anyone help me pls?

Eugen Paraschiv
Guest

Hey Lisdey – sure, this is a good place to ask for help.
That’s an interesting topic – I’m adding it to the Content Calendar of the site – so keep an eye on the RSS feed in about a month. Cheers,
Eugen.

Lisdey Pérez
Guest
Lisdey Pérez

Thanks.. I will do that…

Andy
Guest
Andy

Hi,

Thank you so much for you topic. Could you please help write android client? I am really need an android client to send message to server application.

Thanks,
Andy

Eugen Paraschiv
Guest

Hey Andy,
Unfortunately Android is a bit outside the scope of the site. It’s a huge area and I only have minimal experience with mobile development – which is why I’m not covering it here.
Cheers,
Eugen.

Matthew Concrete Blackmon
Guest

Any chance that the JS client article has come out?

Eugen Paraschiv
Guest

It seems like authors aren’t picking the WebSocket topics, so unfortunately not 🙂
I might be able to put it on my own writing list, but that’s going to be a longer wait.
In the meantime, I’m sure there are lots of writeups out there covering the topic at least partially.
Cheers,
Eugen.

Markus Streicher
Guest
Markus Streicher

Hi @all,

i’ve got a problem with this tutorial. I just get a view with “Welcome to SockJS!”.
I’m working with Spring security and a database login, WebMvc and Thymleaf.

When i add a MVC Config for Path i got a 404 error “localhost:8080/{project}/chat/info” not found.
And when i add also a request mapping in ChatController i got a 500 Server error because i don’t have any template resolver. When adding a template resolver then the 500 error is already there with same message.

Please Help!

Eugen Paraschiv
Guest

Hey Markus, can you open a PR with a failing test on the project? The PR should be very simple – make one of these changes and then have a live test hitting that URL and failing. I’d be happy to have a look. Cheers,
Eugen.

Rohit
Guest
Rohit

Hi,
I am getting the same error. What to do?
Please help.

Eugen Paraschiv
Guest

A PR is the way to go as well.

Rohit
Guest
Rohit

Sorry, What is a PR?

Eugen Paraschiv
Guest

No worries, sorry for using jargon like that. It’s a Pull Request over on Github.