Building A Chat Application With Spring Boot and WebSocket
Building A Chat Application With Spring Boot and WebSocket
In this article, you’ll learn how to use WebSocket API with Spring Boot and build a simple group chat application at the end.
You can explore the live demo of the application by clicking this link - https://spring-ws-chat.herokuapp.com/.
You can just type in your name and start chatting with others. If no one is available in the chat room, then you can open the app in two tabs,
login with different usernames and start sending messages.
Following is a screen shot of the chat application that we’ll be building in this tutorial -
WebSocket is a communication protocol that makes it possible to establish a two-way communication channel between a
server and a client.
WebSocket works by first establishing a regular HTTP connection with the server and then upgrading it to a bidirectional
websocket connection by sending an Upgrade header.
WebSocket is supported in most modern web browsers and for browsers that don’t support it, we have libraries that provide
fallbacks to other techniques like comet and long-polling.
Well, now that we know what websocket is and how it works, let’s jump into the implementation of our chat application.
Let’s use Spring Boot CLI to bootstrap our application. Checkout the Official Spring Boot documentation for instructions on how
to install Spring Boot CLI.
Once you have Spring Boot CLI installed, type the following command in your terminal to generate the project -
$ spring init --name=websocket-demo -dependencies=websocket websocket-demo
If you don’t want to install Spring Boot CLI, No worries, You can use Spring Initializer web tool to generate the project.
Follow the steps below to generate the project using Spring Initializer -
1. Go to http://start.spring.io/.
2. Enter Artifact’s value as websocket-demo.
3. Add Websocket in the dependencies section.
4. Click Generate Project to download the project.
5. Extract the downloaded zip file.
After generating the project, import it into your favorite IDE. The project’s directory structure should look like this -
The first step is to configure the websocket endpoint and message broker. Create a new
package config inside com.example.websocketdemopackage, then create a new class WebSocketConfig inside config package
with the following contents -
package com.example.websocketdemo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.*;
@Configuration
@EnableWebSocketMessageBroker
@Override
registry.addEndpoint("/ws").withSockJS();
@Override
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
In the first method, we register a websocket endpoint that the clients will use to connect to our websocket server.
Notice the use of withSockJS() with the endpoint configuration. SockJSis used to enable fallback options for browsers that
don’t support websocket.
You might have noticed the word STOMP in the method name. These methods come from Spring frameworks STOMP
implementation. STOMP stands for Simple Text Oriented Messaging Protocol. It is a messaging protocol that defines the format
and rules for data exchange.
Why do we need STOMP? Well, WebSocket is just a communication protocol. It doesn’t define things like - How to send a
message only to users who are subscribed to a particular topic, or how to send a message to a particular user. We need STOMP
for these functionalities.
In the second method, we’re configuring a message broker that will be used to route messages from one client to another.
The first line defines that the messages whose destination starts with “/app” should be routed to message-handling methods
(we’ll define these methods shortly).
And, the second line defines that the messages whose destination starts with “/topic” should be routed to the message broker.
Message broker broadcasts messages to all the connected clients who are subscribed to a particular topic.
In the above example, We have enabled a simple in-memory message broker. But you’re free to use any other full-featured
message broker like RabbitMQ or ActiveMQ.
ChatMessage model is the message payload that will be exchanged between the clients and the server. Create a new
package model inside com.example.websocketdemo package, and then create the ChatMessage class inside model package with
the following contents -
package com.example.websocketdemo.model;
CHAT,
JOIN,
LEAVE
return type;
this.type = type;
return content;
this.content = content;
return sender;
this.sender = sender;
We’ll define the message handling methods in our controller. These methods will be responsible for receiving messages from
one client and then broadcasting it to others.
Create a new package controller inside the base package and then create the ChatController class with the following contents
-
package com.example.websocketdemo.controller;
import com.example.websocketdemo.model.ChatMessage;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;
@Controller
@MessageMapping("/chat.sendMessage")
@SendTo("/topic/public")
return chatMessage;
@MessageMapping("/chat.addUser")
@SendTo("/topic/public")
SimpMessageHeaderAccessor headerAccessor) {
headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
return chatMessage;
If you recall from the websocket configuration, all the messages sent from clients with a destination starting with /app will be
routed to these message handling methods annotated with @MessageMapping.
We’ll use event listeners to listen for socket connect and disconnect events so that we can log these events and also broadcast
them when a user joins or leaves the chat room -
package com.example.websocketdemo.controller;
import com.example.websocketdemo.model.ChatMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
@Component
@Autowired
@EventListener
@EventListener
if(username != null) {
chatMessage.setType(ChatMessage.MessageType.LEAVE);
chatMessage.setSender(username);
messagingTemplate.convertAndSend("/topic/public", chatMessage);
We’re already broadcasting user join event in the addUser() method defined inside ChatController. So, we don’t need to do
anything in the SessionConnected event.
In the SessionDisconnect event, we’ve written code to extract the user’s name from the websocket session and broadcast a user
leave event to all the connected clients.
└── css
└── main.css
└── js
└── main.js
└── index.html
The HTML file contains the user interface for displaying the chat messages. It includes sockjs and stomp javascript libraries.
SockJS is a WebSocket client that tries to use native WebSockets and provides intelligent fallback options for older browsers
that don’t support WebSocket. STOMP JS is the stomp client for javascript.
<html>
<head>
</head>
<body>
<noscript>
</noscript>
<div id="username-page">
<div class="username-page-container">
<div class="form-group">
<div class="form-group">
</div>
</form>
</div>
</div>
<div class="chat-container">
<div class="chat-header">
</div>
<div class="connecting">
Connecting...
</div>
<ul id="messageArea">
</ul>
<div class="form-group">
</div>
</div>
</form>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
<script src="/js/main.js"></script>
</body>
</html>
Let’s now add the javascript required for connecting to the websocket endpoint and sending & receiving messages. First, add
the following code to the main.js file, and then we’ll explore some of the important methods in this file -
'use strict';
var colors = [
];
function connect(event) {
username = document.querySelector('#name').value.trim();
if(username) {
usernamePage.classList.add('hidden');
chatPage.classList.remove('hidden');
stompClient = Stomp.over(socket);
event.preventDefault();
function onConnected() {
stompClient.subscribe('/topic/public', onMessageReceived);
stompClient.send("/app/chat.addUser",
{},
connectingElement.classList.add('hidden');
function onError(error) {
connectingElement.textContent = 'Could not connect to WebSocket server. Please refresh this page to try ag
connectingElement.style.color = 'red';
function sendMessage(event) {
var chatMessage = {
sender: username,
content: messageInput.value,
type: 'CHAT'
};
messageInput.value = '';
event.preventDefault();
function onMessageReceived(payload) {
messageElement.classList.add('event-message');
messageElement.classList.add('event-message');
} else {
messageElement.classList.add('chat-message');
avatarElement.appendChild(avatarText);
avatarElement.style['background-color'] = getAvatarColor(message.sender);
messageElement.appendChild(avatarElement);
usernameElement.appendChild(usernameText);
messageElement.appendChild(usernameElement);
textElement.appendChild(messageText);
messageElement.appendChild(textElement);
messageArea.appendChild(messageElement);
messageArea.scrollTop = messageArea.scrollHeight;
function getAvatarColor(messageSender) {
var hash = 0;
return colors[index];
Upon successful connection, the client subscribes to /topic/publicdestination and tells the user’s name to the server by
sending a message to the /app/chat.addUser destination.
The stompClient.subscribe() function takes a callback method which is called whenever a message arrives on the subscribed
topic.
Rest of the code is used to display and format the messages on the screen.
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
html,body {
height: 100%;
overflow: hidden;
body {
margin: 0;
padding: 0;
font-weight: 400;
font-size: 1rem;
line-height: 1.58;
color: #333;
background-color: #f4f4f4;
height: 100%;
body:before {
height: 50%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background: #128ff2;
content: "";
z-index: 0;
.clearfix:after {
display: block;
content: "";
clear: both;
.hidden {
display: none;
.form-control {
width: 100%;
min-height: 38px;
font-size: 15px;
.form-group {
margin-bottom: 15px;
input {
padding-left: 10px;
outline: none;
margin-top: 20px;
margin-bottom: 20px;
h1 {
font-size: 1.7em;
a {
color: #128ff2;
button {
box-shadow: none;
font-size: 14px;
outline: none;
line-height: 100%;
white-space: nowrap;
vertical-align: middle;
border-radius: 2px;
cursor: pointer;
min-height: 38px;
button.default {
background-color: #e8e8e8;
color: #333;
button.primary {
background-color: #128ff2;
color: #fff;
button.accent {
background-color: #ff4743;
color: #fff;
#username-page {
text-align: center;
.username-page-container {
background: #fff;
border-radius: 2px;
width: 100%;
max-width: 500px;
display: inline-block;
margin-top: 42px;
vertical-align: middle;
position: relative;
min-height: 250px;
position: absolute;
top: 50%;
left: 0;
right: 0;
margin: 0 auto;
margin-top: -160px;
.username-page-container .username-submit {
margin-top: 10px;
#chat-page {
position: relative;
height: 100%;
.chat-container {
max-width: 700px;
margin-left: auto;
margin-right: auto;
background-color: #fff;
margin-top: 30px;
max-height: 600px;
position: relative;
#chat-page ul {
list-style-type: none;
background-color: #FFF;
margin: 0;
overflow: auto;
overflow-y: scroll;
#chat-page #messageForm {
padding: 20px;
#chat-page ul li {
line-height: 1.5rem;
margin: 0;
#chat-page ul li p {
margin: 0;
#chat-page .event-message {
width: 100%;
text-align: center;
clear: both;
#chat-page .event-message p {
color: #777;
font-size: 14px;
word-wrap: break-word;
#chat-page .chat-message {
padding-left: 68px;
position: relative;
#chat-page .chat-message i {
position: absolute;
width: 42px;
height: 42px;
overflow: hidden;
left: 10px;
display: inline-block;
vertical-align: middle;
font-size: 18px;
line-height: 42px;
color: #fff;
text-align: center;
border-radius: 50%;
font-style: normal;
text-transform: uppercase;
color: #333;
font-weight: 600;
#chat-page .chat-message p {
color: #43464b;
float: left;
float: left;
width: 80px;
height: 38px;
margin-left: 5px;
.chat-header {
text-align: center;
padding: 15px;
.chat-header h2 {
margin: 0;
font-weight: 500;
.connecting {
padding-top: 5px;
text-align: center;
color: #777;
position: absolute;
top: 65px;
width: 100%;
.chat-container {
margin-left: 10px;
margin-right: 10px;
margin-top: 10px;
.chat-container {
.username-page-container {
width: auto;
margin-left: 15px;
margin-right: 15px;
padding: 25px;
#chat-page ul {
width: 65px;
.chat-header {
padding: 10px;
.connecting {
top: 60px;
.chat-header h2 {
font-size: 1.1em;
You can run the Spring Boot application by typing the following command in your terminal -
$ mvn spring-boot:run
The application starts on Spring Boot’s default port 8080. You can browse the application at http://localhost:8080.
If you want to use a full featured message broker like RabbitMQ instead of the simple in-memory message broker then just add
the following dependencies in your pom.xml file -
<!-- RabbitMQ Starter Dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- Following additional dependency is required for Full Featured STOMP Broker Relay -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</dependency>
Once you’ve added the above dependencies, you can enable RabbitMQ message broker in the WebSocketConfig.java file like
this -
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableStompBrokerRelay("/topic")
.setRelayHost("localhost")
.setRelayPort(61613)
.setClientLogin("guest")
.setClientPasscode("guest");
Congratulations folks! In this tutorial, we built a fully-fledged chat application from scratch using Spring Boot and WebSocket.
You can find the entire code for this application in my github repository. If you liked what you read then give me a star
on githuband share this article with your friends and colleagues.