Building a Real-Time Chatroom with Spring Boot and WebSockets
The Astronomer
Posted on August 16, 2024
In this guide, we will walk through the steps to build a very simple real-time chatroom application using Spring Boot and WebSockets. We will use static HTML and CSS for the frontend. Usernames of users will be randomly generated allowing for anonymous chat.
Overview
Our chatroom application will allow users to join, send messages, and see messages from other users in real-time. We will use Spring Boot for the backend and WebSockets for real-time communication.
Prerequisites
- Java 17 or higher
- Maven
Project Structure
- pom.xml: Maven configuration file with dependencies and plugins.
-
src/main/java/com/chatroom/demo: Contains the main application and configuration files.
- DemoChatroomApplication.java: Main class to run the Spring Boot application.
- config/WebSocketConfig.java: Configuration for WebSocket and STOMP.
- controller/ChatController.java: Controller to handle chat messages.
- model/ChatMessage.java: Model class representing a chat message.
-
src/main/resources/static: Contains static resources like HTML, CSS, and JavaScript files.
- index.html: Main HTML file for the chatroom UI.
- main.css: CSS file for styling the chatroom.
- app.js: JavaScript file for handling WebSocket connections and UI interactions.
Step-by-Step Guide
1. Set Up the Spring Boot Project
Create a new Spring Boot project using Spring Initializr or your preferred method. Add the following dependencies to your pom.xml
:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
2. Create the Main Application Class
Your main application class DemoChatroomApplication.java
should look like this:
package com.chatroom.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoChatroomApplication {
public static void main(String[] args) {
SpringApplication.run(DemoChatroomApplication.class, args);
}
}
3. Configure WebSocket
The WebSocketConfig.java
file is a configuration class for setting up WebSocket messaging and implements the WebSocketMessageBrokerConfigurer interface, which provides methods to configure the message broker and register STOMP (Simple Text Oriented Messaging Protocol) endpoints.
The class WebSocketConfig implements WebSocketMessageBrokerConfigurer, which requires the implementation of two methods: configureMessageBroker
and registerStompEndpoints
.
The configureMessageBroker
method configures the message broker, which is responsible for routing messages from one client to another.
config.enableSimpleBroker("/topic")
enables a simple in-memory message broker with a destination prefix /topic. This is where the server will send messages to clients.
config.setApplicationDestinationPrefixes("/app")
Sets the application destination prefix to /app. This prefix is used to filter destinations targeted to application-specific message-handling methods.
The registerStompEndpoints
method registers the STOMP endpoints, which clients will use to connect to the WebSocket server.
registry.addEndpoint("/ws").withSockJS()
registers an endpoint at /ws
and enables SockJS fallback options. SockJS is a library that provides WebSocket-like communication for browsers that don't support WebSocket.
For this class you'll need @Configuration
and @EnableWebSocketMessageBroker
annotations. @Configuration
indicates that the class is a source of bean definitions and @EnableWebSocketMessageBroker
enables WebSocket message handling, backed by a message broker.
WebSocketConfig.java
:
package com.chatroom.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
}
4. Create the Chat Controller
Create a controller class ChatController.java
:
package com.chatroom.demo.controller;
import com.chatroom.demo.model.ChatMessage;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class ChatController {
@MessageMapping("/chat")
@SendTo("/topic/messages")
public ChatMessage send(ChatMessage message) {
return message;
}
}
5. Create the Chat Message Model
Create a model class ChatMessage.java
:
package com.chatroom.demo.model;
public class ChatMessage {
private String content;
private String username;
// Getters and setters
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
6. Create the Frontend Files
In a Spring Boot application, static resources such as HTML, CSS, and JavaScript files are served automatically from the src/main/resources/static
directory. This behavior is provided by Spring Boot's default configuration.
How It Works
Default Resource Mapping: Spring Boot automatically maps resources from the src/main/resources/static
directory to the root path (/). This means that any file placed in the static directory can be accessed directly via the web browser.
Serving HTML: When you place an index.html
file in the static directory, it will be served as the default page when you navigate to the root URL (http://localhost:8080
).
Use React: You can also use React to build your frontend if you'd like. All you'd have to do is build your React app and place the build files in the Spring Boot static directory. Then run the Spring Boot application to serve the React frontend.
Create the following files in src/main/resources/static
:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Demo Chatroom</title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<div id="chatroom">
<h1>Chatroom</h1>
<div id="messages"></div>
<input type="text" id="message-input" placeholder="Type a message...">
<button onclick="sendMessage()">Send</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.5.1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
<script src="app.js"></script>
</body>
</html>
main.css
#chatroom {
width: 50%;
margin: auto;
text-align: center;
}
#messages {
border: 1px solid #ccc;
height: 300px;
overflow-y: scroll;
margin-bottom: 10px;
}
#message-input {
width: 80%;
padding: 10px;
}
button {
padding: 10px;
}
app.js
var stompClient = null;
var username = generateRandomUsername();
function generateRandomUsername() {
var adjectives = ["Quick", "Lazy", "Happy", "Sad", "Angry"];
var nouns = ["Fox", "Dog", "Cat", "Mouse", "Bear"];
var adjective = adjectives[Math.floor(Math.random() * adjectives.length)];
var noun = nouns[Math.floor(Math.random() * nouns.length)];
return adjective + noun + Math.floor(Math.random() * 1000);
}
function connect() {
var socket = new SockJS('/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/messages', function (message) {
showMessage(JSON.parse(message.body));
});
});
}
function sendMessage() {
var messageContent = document.getElementById('message-input').value;
stompClient.send("/app/chat", {}, JSON.stringify({'username': username, 'content': messageContent}));
document.getElementById('message-input').value = '';
}
function showMessage(message) {
var messagesDiv = document.getElementById('messages');
var messageElement = document.createElement('div');
messageElement.appendChild(document.createTextNode(message.username + ": " + message.content));
messagesDiv.appendChild(messageElement);
}
connect();
- Run the Application Build and run the Spring Boot application:
mvn clean install
mvn spring-boot:run
Open a web browser and navigate to http://localhost:8080
to access the chatroom.
Conclusion
You have successfully built a real-time chatroom application using Spring Boot and WebSockets with static HTML and CSS for the frontend. In future guides, we will enhance the frontend using modern frameworks like React.
A slightly more built-out version can be found here.
Posted on August 16, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.