1. Code
  2. Java

Build a Real-Time Chat Application With Modulus and Spring Boot

This post is part of a series called Getting Started with Modulus.
Build a Real-Time Chat Application With Modulus and Python
The Hitchhiker's Guide to Docker and Modulus
Sponsored Content

This sponsored post features a product relevant to our readers while meeting our editorial guidelines for being objective and educational.

In this tutoral, we will use Spring Boot for the web development environment, Websockets for real-time communication, Tomcat for the Java application container, Gradle for building and managing the dependencies, Thymeleaf for template rendering, MongoDB for data storage, and finally there will be no XML for bean configurations. Just to make you inspired, at the end of this article, you will see a fully working application like the one shown below.

Realtime Chat Application Chat Screen

1. Scenario

  1. Doe opens the chat page to communicate with his friends.
  2. He is prompted to choose a nickname.
  3. He enters the chat page and sends a message. The message is sent to the Spring MVC endpoint to be saved to the database and broadcast.
  4. The specified endpoint handles the message and broadcasts that message to all clients connected to the chat system.

2. Build Dependencies and Gradle Configuration

Before proceeding with the internal structure of the project, let me explain which libraries we will use for the project features listed above, and manage them by using Gradle. When you clone the project from GitHub, you will see a file called build.gradle in the project root directory as below.

I will not dive into the Gradle internals, but let me explain the parts that we need for our project. Spring Boot is built mainly for developing standalone applications in jar format. In our project, we will generate a war project instead of jar. That is because Modulus needs a war file to deploy the project automatically to its cloud. 

In order to generate a war file, we have used apply plugin: 'war'. Modulus also expects the war name to be ROOT.war by default, and that is why we have used:

When you run the Gradle build task, it will generate a war file to deploy to the Tomcat container. And finally, as you can guess, the dependencies section is for third-party libraries for specific actions. 

That is all for the project dependencies section, and you can refer to the Gradle user guide for more about Gradle.

3. Software Design

If you want to develop a good application, it is best practice to define your project structure in small pieces. You can see the pieces of the entire architecture of our application.

3.1. Model

We are developing a chat application, so we can say that we have a ChatMessageModel model (i.e. domain object). While we are saving or viewing the chat message detail, we can cast the chat object from or to this ChatMessageModel model. Also, we can use the User model for chat users, but to make the application simpler, we will use just nickname as text. The ChatMessageModel model has the following fields: text, author, and createDate. The class representation of this model is as follows:

This domain object helps us to represent the chat message as JSON when needed. Our model is OK, so let's continue with the controllers.

3.2. Controller

The controller is the behavior of your application. This means you need to keep your controller simple and capable of easy interaction with domain models and other services. We are expecting our controllers to handle:

  1. Chat message save requests
  2. Listing the latest chat messages
  3. Serving the chat application page
  4. Serving the login page
  5. Broadcasting chat messages to clients

Here you can see the overall endpoints:

The first and second endpoints are just for serving the login and main chat page. The third action is for handling new chat message storage and broadcasting. After the message is stored, it will be notified to clients through the /topic/message channel. To store message data to MongoDB, we will use a MongoDB repository.  

As you can see, there are two types of endpoint /messages: GET and POST. When you make a POST request to endpoint /messages with proper message payload, it will be automatically cast to the ChatMessageModel class, and the message will be saved to MongoDB. After successful saving, it will be automatically pushed to the clients. But, how? In that action, there is an annotation @SendTo("/topic/newMessage"). This will send the content returned from the function to the clients. And the returned content is like below:

This is the latest message from the database:

Sample JSON message

The above message will be converted to a format for WebSocket communication. This channel message will be handled on the client side with a third-party JavaScript library, and it will be handled in the following sections. 

For message db operations, spring-boot-starter-data-mongodb is used. This library helps us for repository operations, and to create a repository object for MongoDB is very simple. You can see the example ChatMessageRepository below:

If you create an interface and extend MongoRepository<?, String>, you will be able to automatically use CRUD operations like find(), findAll(), save(), etc. 

As you can see, MongoRepository expects a domain object. We have already defined this model in the Model section of the tutorial. In this repository, we have defined a custom function called findAllByOrderByCreateDateAsc()

If you have ever used JPA before you can understand this easily, but let me explain this briefly. If you define a function name in an interface that extends MongoRepository, this function name will be parsed to a query on the back end by Spring automatically. It will be something like:

In ChatMessageController, we used this function, and also we have used the default functions of the MongoRepository:

findAll is used a parameter for sorting and pagination. You can have a look at the guide on the Spring website for more details about Spring JPA.

3.3. View

In the view part, we have only two pages. One of them is the login page, to get the nickname of the user, and the second one is the main chat page to send messages to chat users. 

As you can see in the controller section above, they are rendered by using two endpoints, /login and /chatTo create interactive pages, we will use some third-party JavaScript libraries. We will use them from CDN pages. You can see the login page below:

On the login page, we have a sample nickname text box. When you click Enter Chat, your nickname will be saved to a cookie. This nickname will be used to set the chat message author field. When you click Enter Chat, the chat page will be opened. If you are already logged in and go to the login page, you will be redirected to the chat page. 

Here's the chat page:

This page is for simply viewing and sending messages. Messages are delivered to this page via WebSockets. On this page you can see sockjs and stompjs. Those are for handling notifications. Whenever a new message comes, the latest messages area is repopulated. 

By the way, when you first open the chat page, the latest messages will be retrieved in the messages area. As you can see on the JavaScript side, our message channel is newMessage. So, we are listening to this channel, and when you click the Send button, the message in the text box will be sent to the endpoint and that message will be broadcast to the connected clients after successful storage.

As you can see, the software architecture here is very simple and easy to develop. We have production-ready code, and let's deploy it to Modulus.

Modulus is one of the best PaaS for deploying, scaling and monitoring your application in the language of your choice.

4. Deployment 

4.1. Prerequisites

Before deploying the application, let's create a database by using the Modulus admin panel. You need a Modulus account for dba creation and application deployment, so please create an account if you don't have one. 

Go to the Modulus dashboard and create a database:

Database creation

On the database creation screen please provide a database name, select the MongoDB version (I have used 2.6.3, so it will be better if you choose 2.6.3 too), and finally define a user to apply database read/write operations. 

Create Database

You can get a MongoDB URL after successfully creating the database. We will use the MongoDB URL in the environment variables to be used by the Spring Boot application.

To set the environment variables for MongoDB, you need to have an application. Go to Dashboard and click Projects. On this page, click Create New Project.

To continue with configuring the environment variables, please go to Dashboard and click Projects. Select your project, and click Administration. Scroll down the page, and set environment variables with the key SPRING_DATA_MONGODB_URI and value of your database URI:

Environment Variables

When you deploy your application, Spring will use that environment variable value. We have done with the requirements, and let's continue with the deployment part.

4.2. Deployment With CLI

In order to deploy the project, run a gradle build task:

This task will generate a war file called ROOT.war. Copy this file to a fresh folder and install modulus CLI if you haven't.

Log in to the system;

Now execute the following command to deploy ROOT.war to Modulus.

This will deploy the war file and you can tail project logs to see the status of your deployment by executing the following command:

That's all with the deployment!

5. Conclusion

The main purpose of this tutorial is to show you how to create a real-time chat application with Spring Boot, WebSockets, and MongoDB. 

In order to run the project in production, Modulus is used as a PaaS provider. Modulus has very simple steps for deployment, and it also has an internal database (MongoDB) for our projects. Beside this, you can use very helpful tools in the Modulus dashboard like Logs, Notifications, Auto-Scaling, Db Administration, and more.

Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.