WebSocket – Create Spring WebSocket Application with SpringBoot + SockJS + STOMP

The WebSocket protocol provides new capability for web applications: full-duplex, two-way communication. So in the system where the client and server need to exchange data at high frequency and with low latency, WebSocket is the best solution. That is the reason for us to create a Spring WebSocket Application with JavaSampleApproach.

Related Post: Spring Boot WebSocket with Angular 5 Client | SockJS + STOMP

I. Spring WebSocket Application

1. Flow of messages

We create a Spring WebSocket Application with the below flow of messages:

spring-websocket-architecture-ws

Detail explanations:
– WebSocket clients connect to the WebSocket endpoint at ‘/jsa-stomp-endpoint’
– Subscriptions to ‘/topic/hi’ pass through the response channel, then are forwarded to the In-memory broker (Simple Broker).
– User objects sent to ‘/jsa/hello’ pass through the request channel then are forwarded to the spring WebController. WebController will handle User objects by @MessageMapping and transform to Hello messages then use @SendTo returns the messages to ‘/topic/hi’ through the brokerChannel.


...

@MessageMapping("/hello")
@SendTo("/topic/hi")
public Hello greeting(User user) throws Exception {
    return new Hello(...);
}

...

– The Simple Broker broadcasts messages to subscribers through the response channel.

2. Server side

In server side, we use SockJS and STOMP for our application.

What is SockJS?
-> SockJS lets applications use a WebSocket API but falls back to non-WebSocket alternatives when necessary at runtime, without the need to change application code.

Simple Java configuration to enable SockJS and Stomp in Spring application:


@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/jsa-stomp-endpoint").withSockJS();
    }
}

*Update: with new Spring WebSocket release, AbstractWebSocketMessageBrokerConfigurer is deprecated, we will implement the WebSocketMessageBrokerConfigurer interface instead:


@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

	@Override
	public void configureMessageBroker(MessageBrokerRegistry config) {
		// ...
	}

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		// ...
	}
}

3. Client side

About client side, we uses {sockjs-client, stomp-websocket} libs for development:

<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>

– Make a connection:


function connect() {
    var socket = new SockJS('/jsa-stomp-endpoint');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        setConnected(true);
        stompClient.subscribe('/topic/hi', function (hello) {
			...
        });
    });
}

The connect() function uses SockJS and stomp.js to open a connection to /jsa-stomp-endpoint, which is where our SockJS server is waiting for connections.

– Dis-Connection:


function disconnect() {
    if (stompClient != null) {
        stompClient.disconnect();
    }
    setConnected(false);
}

– Send messages:


function sendName() {
    stompClient.send("/jsa/hello", {}, JSON.stringify({'name': $("#name").val()}));
}

II. Practice

0. Technologies

– Java 8
– Maven 3.6.1
– Spring Tool Suite: Version 3.8.4.RELEASE
– Spring Boot: 1.5.4.RELEASE
– Spring WebSocket
– SockJS
– Stomp
– JQuery
– Bootstrap

1. Server side

1.1 Create SpringBoot project

Using Spring Tool Suite to create a Spring Starter Project, then add dependencies:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>webjars-locator</artifactId>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>sockjs-client</artifactId>
  <version>1.0.2</version>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>stomp-websocket</artifactId>
  <version>2.3.3</version>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>bootstrap</artifactId>
  <version>3.3.7</version>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>jquery</artifactId>
  <version>3.1.0</version>
</dependency>

1.2 Create models

Create 2 message models {User, Hello}

– User:


package com.javasampleapproach.websocket.model;

public class User {
	private String name;
	
	public User(){}
	
	public void setName(String name){
		this.name = name;
	}
	
	public String getName(){
		return this.name;
	}
}

– Hello:


package com.javasampleapproach.websocket.model;

public class Hello {
	private String greeting;
	
	public Hello(){}
	
	public Hello(String greeting){
		this.greeting = greeting;
	}
	
	public void setGreeting(String greeting){
		this.greeting = greeting;
	}
	
	public String getGreeting(){
		return this.greeting;
	}
}

1.3 Configure SockJS and STOMP messaging


package com.javasampleapproach.websocket.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/jsa-stomp-endpoint").withSockJS();
    }
}

@EnableWebSocketMessageBroker enables WebSocket message handling, backed by a message broker.
enableSimpleBroker() enables a simple memory-based message broker.
registerStompEndpoints() is for registering the /jsa-stomp-endpoint endpoint, enabling SockJS fallback options – an alternate transports in case WebSocket is not available.

1.4 Create WebController


package com.javasampleapproach.websocket.controller;

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

import com.javasampleapproach.websocket.model.Hello;
import com.javasampleapproach.websocket.model.User;

@Controller
public class WebController {
	
	@MessageMapping("/hello")
	@SendTo("/topic/hi")
	public Hello greeting(User user) throws Exception {
	    return new Hello("Hi, " + user.getName() + "!");
	}
}

As above explanation, User objects are sent to /jsa/hello pass through the request channel then are forwarded to the Spring WebController.

WebController will handle User objects by @MessageMapping and transform to Hello messages then use @SendTo to return the messages to ‘/topic/hi’ through the brokerChannel.

2. Client side

2.1 Create index.html

<!DOCTYPE html>
<html>
<head>
    <title>Spring Boot WebSocket!</title>
    <link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <link href="/main.css" rel="stylesheet">
    <script src="/webjars/jquery/jquery.min.js"></script>
   <script src="/webjars/sockjs-client/sockjs.min.js"></script>
   <script src="/webjars/stomp-websocket/stomp.min.js"></script>
    <script src="/app.js"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-4">
            <form class="form-inline">
                <div class="form-group">
                    <label for="connect">Make Connection:</label>
                    <button id="connect" class="btn btn-default" type="submit">Connect</button>
                    <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
                    </button>
                </div>
            </form>
        </div>
        <div class="col-md-4">
            <form class="form-inline">
                <div class="form-group">
                    <label for="name">User's Name:</label>
                    <input type="text" id="name" class="form-control">
                </div>
                <button id="send" class="btn btn-default" type="submit">Send</button>
            </form>
        </div>
    </div>
    <div class="row">
        <div class="col-md-3">
            <table id="conversation" class="table table-striped">
                <thead>
	                <tr>
	                    <th>Greetings</th>
	                </tr>
                </thead>
                <tbody id="hellos">
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>

2.2 Implement WebSocket javascript functions

We implement 3 main functions for connection, disconnection and send messages:


var stompClient = null;

function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
    }
    else {
        $("#conversation").hide();
    }
    $("#hellos").html("");
}

function connect() {
    var socket = new SockJS('/jsa-stomp-endpoint');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/hi', function (hello) {
            showGreeting(JSON.parse(hello.body).greeting);
        });
    });
}

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

function sendName() {
    stompClient.send("/jsa/hello", {}, JSON.stringify({'name': $("#name").val()}));
}

function showGreeting(greeting) {
    $("#hellos").append("" + greeting + "");
}

$(function () {
    $("form").on('submit', function (e) {
        e.preventDefault();
    });
    $( "#connect" ).click(function() { connect(); });
    $( "#disconnect" ).click(function() { disconnect(); });
    $( "#send" ).click(function() { sendName(); });
});

3. Run and check results

Build and Run the SpringBoot project with command-lines: mvn clean install and mvn spring-boot:run

3.1 Make a connection

Make a request at http://localhost:8080/

-> Result:

spring websocket application- index

3.2 Send messages

Press Connet button, and input JavaSampleApproach, then press Send button:

-> Result:

spring websocket application - index - first send

Open a new tab, make a request http://localhost:8080/, press Connet button, input JSA, then press Send button:

-> Result:

3.3 Dis-connection

Press Disconnect a client, all greetings messages at the client are removed.

-> Result:

spring websocket architecture - index - 3 send.png

III. Source code

SpringWebSocket

45 thoughts on “WebSocket – Create Spring WebSocket Application with SpringBoot + SockJS + STOMP”

  1. Tried this guide, but I only get CORS error:

    Access to XMLHttpRequest at ‘http://localhost:8080/test-stomp-endpoint/info?t=1544554396549’ from origin ‘http://localhost:4200’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

    1. This might be a little too late but for anyone else with this issue, the following works:

      @Override
      public void registerStompEndpoints(StompEndpointRegistry registry) {
          registry.addEndpoint("/jsa-stomp-endpoint").setAllowedOrigins("*").withSockJS();
      }
      

      This is allowing all origins though.

  2. I can’t find a way to pass the token on SockJS handshake. I am searching it for weeks and no one seems to have a solution.

  3. You could definitely see your enthusiasm in the work you write.
    The world hopes for more passionate writers such as
    you who aren’t afraid to mention how they believe.
    Always go after your heart.

  4. I’m really enjoying the design and layout of your site.

    It’s a very easy on the eyes which makes it much
    more pleasant for me to come here and visit more often. Did you hire out
    a designer to create your theme? Exceptional work!

  5. Woah! I’m really loving the template/theme of
    this site. It’s simple, yet effective. A lot of times it’s tough to get that “perfect balance” between superb usability
    and visual appeal. I must say you have done a amazing job with this.
    Also, the blog loads very quick for me on Safari.
    Superb Blog!

  6. Thanks for ones marvelous posting! I truly enjoyed
    reading it, you might be a great author.I will make certain to bookmark
    your blog and definitely will come back down the road.
    I want to encourage continue your great job, have a nice evening!

  7. My spouse and I absolutely love your blog and find many of your post’s
    to be what precisely I’m looking for. Do you offer guest writers to write
    content for you personally? I wouldn’t mind writing a post or elaborating on most of the subjects you
    write regarding here. Again, awesome web site!

  8. Hello, i read your blog from time to time and i own a similar one and i was just wondering if you get a lot of spam responses?

    If so how do you stop it, any plugin or anything you can recommend?
    I get so much lately it’s driving me mad so any help is very much appreciated.

  9. This design is spectacular! You obviously know how to keep
    a reader entertained. Between your wit and your videos, I was almost moved to start my own blog (well, almost…HaHa!) Excellent job.

    I really loved what you had to say, and more than that, how you presented it.
    Too cool!

  10. I’m not sure where you’re getting your information, but good topic.
    I needs to spend some time learning much more or understanding more.
    Thanks for great information I was looking for this info for my mission.

  11. It’s a shame you don’t have a donate button! I’d certainly donate
    to this brilliant blog! I guess for now i’ll settle for bookmarking and adding your RSS feed to my Google account.
    I look forward to new updates and will share this website with
    my Facebook group. Talk soon!

  12. 948647 5612Hoping to go into business venture world-wide-web Indicates revealing your products or services furthermore companies not only to ladies locally, but nevertheless , to numerous prospective clients in which are online in most cases. e-wallet 277829

  13. Magnificent beat ! I would like to apprentice whilst you amend your
    web site, how can i subscribe for a weblog website?
    The account aided me a acceptable deal. I have been a little bit familiar
    of this your broadcast provided brilliant transparent idea

  14. Heya! I just wanted to ask if you ever have any problems with hackers?
    My last blog (wordpress) was hacked and I ended up losing many months of
    hard work due to no back up. Do you have any solutions to prevent hackers?

  15. Hello, i believe that i saw you visited my website thus i came to go back the choose?.I’m trying to in finding issues to enhance my website!I assume its adequate
    to use a few of your concepts!!

  16. Wow that was unusual. I just wrote an incredibly long comment but after
    I clicked submit my comment didn’t appear. Grrrr… well I’m not writing
    all that over again. Anyhow, just wanted to say great blog!

  17. Today, I went to the beach with my kids. I found a sea shell and gave it to
    my 4 year old daughter and said “You can hear the ocean if you put this to your ear.” She put
    the shell to her ear and screamed. There was a hermit crab inside
    and it pinched her ear. She never wants to go back!

    LoL I know this is totally off topic but I had to tell someone!

  18. Hi I am so glad I found your webpage, I really found you by mistake, while I
    was researching on Aol for something else, Nonetheless I am
    here now and would just like to say thank you for a
    fantastic post and a all round exciting blog (I also love the theme/design), I don’t have time to go
    through it all at the moment but I have saved it and also added
    in your RSS feeds, so when I have time I will be back to read a great deal more, Please do
    keep up the superb jo.

  19. Wonderful goods from you, man. I’ve understand your stuff previous to and you
    are just too wonderful. I actually like what you’ve
    acquired here, really like what you are stating and the way in which you say it.
    You make it entertaining and you still care for to keep it sensible.

    I can not wait to read far more from you. This is actually a wonderful site.

  20. Hi everybody, here every one is sharing these kinds of familiarity,
    so it’s nice to read this webpage, and I used to pay a quick
    visit this website daily.

  21. Hi there! I know this is kind of off-topic
    but I needed to ask. Does running a well-established website such as yours require a lot
    of work? I am completely new to writing a blog however I do
    write in my journal everyday. I’d like to start a blog so I will be able to share my experience and feelings
    online. Please let me know if you have any ideas or tips for new
    aspiring bloggers. Thankyou!

Leave a Reply

Your email address will not be published. Required fields are marked *