DEV Community

The Astronomer
The Astronomer

Posted on

Building a Real-Time Chatroom with Spring Boot and WebSockets

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>
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
  1. Run the Application Build and run the Spring Boot application:
mvn clean install
mvn spring-boot:run
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)