Building a Real-Time Chat Application with WebSockets using Spring Boot
In today's fast-paced world, real-time communication is essential for many applications. Whether it's a chat application, a live notification system, or a collaborative platform, the ability to push updates to clients in real time is a crucial feature. In this blog post, we'll explore how to build a real-time chat application using WebSockets with Spring Boot.
Understanding WebSockets
WebSockets provide a full-duplex communication channel over a single, long-lived connection between clients and servers. Unlike traditional HTTP requests, where the client initiates communication, WebSockets allows both the client and server to send messages to each other asynchronously.
Setting Up the Project
We'll use Spring Boot to build our real-time chat application. Spring Boot makes it easy to create stand-alone, production-grade Spring-based applications. To get started, make sure you have Spring Boot installed and configured in your development environment.
Project Structure
Our project will consist of the following components:
- Message: A POJO class representing a chat message.
- MessageController: Handles incoming WebSocket messages and broadcasts them to all connected clients.
- Config: Configuration class for WebSocket and message broker.
- WebSocketEventListener: Listens for WebSocket events such as connect and disconnect.
Let's dive into the implementation details.
Pom.xml
<!-- Spring Boot Starter for WebSocket support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Message Class
The Message
class is a Plain Old Java Object (POJO) used to represent a chat message. It includes fields such as name
(sender's name), message
(actual message content), time
(timestamp of the message), and type
(type of message, e.g., "LEFT" when a user leaves the chat).
package com.anucodes.chatapp.models;
import lombok.*;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Message {
private String name;
private String message;
private LocalDateTime time;
private String type;
}
MessageController
The MessageController
class handles incoming WebSocket messages and serves as the entry point for client-server communication. It includes two methods:
register
: Handles registration messages from clients, stores user information, and broadcasts user connection messages to all clients.sendMessage
: Handles messages sent by clients, adds timestamp information, and broadcasts them to all clients.
package com.anucodes.chatapp.controller;
import com.anucodes.chatapp.models.Message;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.List;
@RestController
@CrossOrigin()
@RequiredArgsConstructor
public class MessageController {
@MessageMapping("/chat.register")
@SendTo("/topic/public")
public Message register(Message message, SimpMessageHeaderAccessor headerAccessor) throws InterruptedException {
message.setTime(LocalDateTime.now());
return message;
}
@MessageMapping("/chat.send")
@SendTo("/topic/public")
public Message sendMessage(Message message) {
message.setTime(LocalDateTime.now());
return message;
}
}
Config Class
The Config
class is a configuration class for WebSocket and message broker settings. It enables WebSocket support and configures the message broker to enable communication between clients and the server.
package com.anucodes.chatapp.config;
import org.springframework.context.annotation.Bean;
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 Config implements WebSocketMessageBrokerConfigurer {
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/server").setAllowedOriginPatterns("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
WebSocketEventListener
The WebSocketEventListener
class listens for WebSocket events, such as user disconnection. When a user disconnects, it removes the user from the session map, generates a message indicating that the user has left the chat, and broadcasts this message to all connected clients.
package com.anucodes.chatapp.config;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
import org.springframework.messaging.support.GenericMessage;
import java.time.LocalDateTime;
import java.util.List;
@Component
@RequiredArgsConstructor
public class WebSocketEventListener {
private final SimpMessageSendingOperations messagingTemplate;
@EventListener
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
String sessionId = (String) event.getMessage().getHeaders().get("simpSessionId");
String username = userSessionMap.stream()
.filter(user -> sessionId.equals(userSessionMap.get(user)))
.findFirst().orElse(null);
if (username != null) {
System.out.println("user disconnected: " + username);
var chatMessage = Message.builder()
.type("LEFT")
.name(username)
.time(LocalDateTime.now())
.build();
messagingTemplate.convertAndSend("/topic/public", chatMessage);
}
}
}
Conclusion
In this blog post, we've explored how to build a real-time chat application using WebSockets with Spring Boot. We've covered the implementation of WebSocket endpoints, message handling, and broadcasting messages to connected clients. With WebSockets, you can create interactive and responsive applications that provide a seamless user experience. Feel free to extend this example further by adding features such as user authentication, message history, or private messaging. Happy coding!