rhondamuse.com

Creating a Web Chat Application with Social Login Using Spring Boot

Written on

Chapter 1: Introduction

This guide is part of a series dedicated to building a Web Chat Application that integrates social login functionality using Spring Boot.

In the introductory piece, we lay out the sections we will cover and provide a sneak peek of the final outcome of our Web Chat application!

In this installment, our focus will be on backend development, covering key topics like WebSocket configuration, message payloads, and the application controller.

Let's dive in!

Implementing the Backend

Step 1: Setting Up WebSocket Configuration

To kick off, we need to create the necessary backend classes. First, let’s establish our WebSocket configuration.

We will create a Java class named WebSocketConfig within the websocket package:

package com.example.simplewebchat.websocket;

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 registerStompEndpoints(StompEndpointRegistry registry) {

registry.addEndpoint("/websocket").withSockJS();

}

@Override

public void configureMessageBroker(MessageBrokerRegistry registry) {

registry.setApplicationDestinationPrefixes("/app");

registry.enableSimpleBroker("/topic");

}

}

The @Configuration annotation indicates that this class provides application configuration.

The @EnableWebSocketMessageBroker annotation enables WebSocket message handling and sets up a message broker for routing messages.

Implementing the WebSocketMessageBrokerConfigurer interface allows us to customize WebSocket settings via its methods.

The registerStompEndpoints method establishes the WebSocket endpoint that clients can connect to, specifically /websocket, with withSockJS providing fallback options for browsers lacking native WebSocket support.

The configureMessageBroker method configures the message broker, specifying that any messages prefixed with /app will reach the application's message-handling methods, while enabling a simple message broker for broadcasting to subscribed clients on /topic.

Step 2: Defining WebSocket Message Payload

Next, we will define the WebSocket message payload for our Web Chat, which will include user information and their actions.

The user actions we will track are:

  • User joined the chat
  • User commented in the chat
  • User left the chat

To represent these actions, we will create a Java Enum named Action:

package com.example.simplewebchat.websocket;

public enum Action {

JOINED, COMMENTED, LEFT

}

Next, we will create a record in the websocket package to store user information, including their ID, nickname, and avatar ID:

package com.example.simplewebchat.websocket.message;

public record User(String id, String nickname, String avatarId) {

}

Finally, we will create a record for our message:

package com.example.simplewebchat.websocket;

import java.time.Instant;

public record Message(User user, String comment, Action action, Instant timestamp) {

}

This record contains fields for the user, their comment, the action associated with the message, and the timestamp of when the message was created.

Step 3: Creating the UI Controller

Now, we will create a Java class called UIController inside the ui package:

package com.example.simplewebchat.ui;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.GetMapping;

@Controller

public class UIController {

@GetMapping("/")

public String index() {

return "index";

}

}

The UIController class acts as a Spring MVC controller responsible for handling GET requests to the root path ("/").

When the root path is accessed, the index method is called, returning the string "index", which represents the logical name of the Thymeleaf template to be rendered.

In the next tutorial, we will shift our focus to the frontend implementation, including creating the index.html file.

Step 4: Setting Up the WebSocket Controller

Next, we will create a Java class named WebSocketController within the websocket package:

package com.example.simplewebchat.websocket;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.context.event.EventListener;

import org.springframework.messaging.handler.annotation.MessageMapping;

import org.springframework.messaging.handler.annotation.Payload;

import org.springframework.messaging.simp.SimpMessageHeaderAccessor;

import org.springframework.messaging.simp.SimpMessagingTemplate;

import org.springframework.messaging.simp.stomp.StompHeaderAccessor;

import org.springframework.stereotype.Controller;

import org.springframework.web.socket.messaging.SessionDisconnectEvent;

import java.time.Instant;

import java.util.LinkedHashSet;

import java.util.Map;

import java.util.Set;

@Controller

public class WebSocketController {

private static final Logger log = LoggerFactory.getLogger(WebSocketController.class);

private final SimpMessagingTemplate simpMessagingTemplate;

private final Set<User> onlineUsers = new LinkedHashSet<>();

public WebSocketController(SimpMessagingTemplate simpMessagingTemplate) {

this.simpMessagingTemplate = simpMessagingTemplate;

}

@MessageMapping("/chat")

public void handleChatMessage(@Payload Message message, SimpMessageHeaderAccessor headerAccessor) {

simpMessagingTemplate.convertAndSend("/topic/all/messages", message);

if (Action.JOINED.equals(message.action())) {

String userDestination = String.format("/topic/%s/messages", message.user().id());

onlineUsers.forEach(onlineUser -> {

Message newMessage = new Message(onlineUser, null, Action.JOINED, null);

simpMessagingTemplate.convertAndSend(userDestination, newMessage);

});

headerAccessor.getSessionAttributes().put("user", message.user());

onlineUsers.add(message.user());

}

}

@EventListener

public void handleSessionDisconnectEvent(SessionDisconnectEvent event) {

StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());

Map<String, Object> sessionAttributes = headerAccessor.getSessionAttributes();

if (sessionAttributes == null) {

log.error("Unable to get the user as headerAccessor.getSessionAttributes() is null");

return;

}

User user = (User) sessionAttributes.get("user");

if (user == null) {

return;

}

onlineUsers.remove(user);

Message message = new Message(user, "", Action.LEFT, Instant.now());

simpMessagingTemplate.convertAndSend("/topic/all/messages", message);

}

}

The WebSocketController class plays a crucial role in managing WebSocket events and processing messages in the Web Chat application.

By using constructor injection, we inject the SimpMessagingTemplate to facilitate message dispatch to WebSocket destinations. We maintain a simple in-memory list of users participating in the chat, with the onlineUsers variable representing a set of currently active users.

This application will utilize two topics:

  • /topic/all/messages: for broadcasting messages to all users.
  • /topic/{userId}/messages: for sending messages to a specific user.

The handleChatMessage method manages incoming WebSocket messages sent to the "/chat" destination. It accepts a Message payload and a SimpMessageHeaderAccessor. The received message is broadcasted to all subscribed clients using simpMessagingTemplate.convertAndSend. If the action is "JOINED," additional messages are sent to the newly joined user to provide an updated list of online participants.

When a WebSocket session is disconnected, the handleSessionDisconnectEvent method is triggered, which listens for session disconnect events. Upon receiving such an event, user information is extracted from session attributes. The user is removed from the onlineUsers set, and a message is sent to inform other users of the disconnection.

Next Steps

In the upcoming tutorial, we will implement the frontend of the Web Chat application.

We hope you are finding this Web Chat Tutorial series engaging!

Support and Engagement

If you enjoyed this article and would like to express your support, consider taking the following actions:

πŸ‘ Engage by clapping, highlighting, and replying to my story. I'm happy to answer any questions;

🌐 Share my story on Social Media;

πŸ”” Follow me on: Medium | LinkedIn | Twitter;

βœ‰οΈ Subscribe to my newsletter to stay updated on my latest posts.

This first video, titled "Spring Boot & WebSockets: Build a Real-Time Chat App From Scratch," provides an insightful introduction to creating a real-time chat application using Spring Boot and WebSockets.

The second video, "OAuth2 Social Login with Spring Security | 'Sign in with Google' Spring Boot Tutorial," offers a comprehensive guide on implementing social login features in your Spring Boot application.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Innovative Approaches to Hydrogen Production from Water

Exploring new catalysts and materials for efficient hydrogen generation from water.

Exploring Earning Potential in the Metaverse Landscape

Discover how Web 3.0 economies are set to reshape industries, focusing on gaming, social media, and design in the Metaverse.

Understanding What Women Notice About You That You Overlook

Discover the key traits women notice in men that often go unnoticed.