A demonstration application that implements WebSockets with Spring Boot to create a real-time chat application.
This project is a simple demonstration of how to implement WebSockets in a Spring Boot application. It allows users to connect to a chat room and send messages that are transmitted in real-time to all connected users. The application is fully internationalized and supports multiple languages. Messages are persisted in an H2 database for history retention.
- Real-time communication using WebSockets
- Simple user interface for sending and receiving messages
- User connection management
- Client-side internationalization (i18n) with JSON translation files
- Multiple language support (English, Catalan)
- Message types for chat, join/leave notifications, and errors
- Message persistence with H2 database
- Database schema management with Flyway migrations
- Externalized WebSocket configuration
- Message history display when joining the chat
- Lightweight and easy to understand codebase
- Comprehensive test suite for both unit and integration testing
- Microservices-friendly architecture with frontend/backend separation
- Improved user experience with visual separation of username and timestamp
- Prevention of duplicate join messages
- Dynamic UI updates when changing language
- In-memory database configuration for development
- Robust XSS protection and message validation
- Connect/Disconnect functionality with dynamic button state
- Dynamic button activation based on input validation
- Message formatting options with HTML tags
- Improved error handling and display
- Java 21 or higher
- Maven 3.6+ or use the included Maven wrapper
- A modern web browser that supports WebSockets
- Java 21
- Spring Boot 3.4.4
- Spring WebSocket
- Spring Data JPA
- H2 Database
- Flyway for database migrations
- HTML/CSS/JavaScript (frontend)
- Modern JavaScript Intl API for internationalization
- JSON for translation files
- YAML for application configuration
- JUnit 5 and Mockito for testing
- Docker for deployment
- OWASP Java HTML Sanitizer for XSS protection
- Apache Commons Text for HTML escaping
src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── springbootwebsocket
│ │ ├── ChatMessage.java (Message model/entity)
│ │ ├── ChatMessageHandler.java (Chat message handler)
│ │ ├── MessageUtils.java (Internationalization utils)
│ │ ├── config (Configuration directory)
│ │ ├── controller
│ │ │ ├── ChatMessageController.java (REST API for message history)
│ │ │ └── HomeController.java (Home page controller)
│ │ ├── repository
│ │ │ └── ChatMessageRepository.java (JPA repository for messages)
│ │ ├── security
│ │ │ └── MessageValidator.java (XSS protection and message validation)
│ │ ├── service
│ │ │ └── ChatMessageService.java (Service for message operations)
│ │ ├── SpringBootWebSocketApplication.java (Main class)
│ │ └── WebSocketConfig.java (WebSocket configuration)
│ └── resources
│ ├── application.yml
│ ├── application-docker.yml
│ ├── db
│ │ └── migration
│ │ ├── V1__Create_chat_messages_table.sql (Flyway migration script)
│ │ └── V2__Add_count_column_to_chat_messages.sql (Adds count column for user count)
│ ├── i18n
│ │ └── messages_en.properties (Backend messages for system logs)
│ └── static
│ ├── css
│ │ └── styles.css (Application styles)
│ ├── i18n
│ │ ├── messages_en.json (English translations)
│ │ └── messages_ca.json (Catalan translations)
│ ├── js
│ │ ├── chat.js (Chat functionality)
│ │ └── i18n.js (Internationalization logic)
│ └── index.html (Chat user interface)
└── test
├── java
│ └── com
│ └── example
│ └── springbootwebsocket
│ ├── ChatMessageHandlerTest.java (Unit tests for handler)
│ ├── ChatMessageTest.java (Tests for message model)
│ ├── DirectHtmlIntegrationTest.java (Direct HTML file tests)
│ ├── MessageUtilsTest.java (Tests for i18n utils)
│ ├── SimpleHtmlTest.java (Simple HTML structure tests)
│ ├── TestWebSocketConfig.java (Test-specific WebSocket config)
│ ├── WebSocketConfigTest.java (Unit tests for config)
│ └── controller
│ └── ChatMessageControllerTest.java (Unit tests for REST API)
└── resources
└── application-test.yml (Consolidated test-specific properties)
- Clone this repository
- Navigate to the project directory
- Run the application with the Maven wrapper:
./mvnw spring-boot:run
If you have Maven installed:
mvn spring-boot:run
./mvnw clean package
java -jar target/spring-boot-web-socket-1.3.0-SNAPSHOT.jar
- Open your browser and go to
http://localhost:8080
- Enter your name in the text field
- Click "Connect" to join the chat (button will be disabled until a valid name is entered)
- The application will load and display message history
- Type a message in the bottom text field
- Click "send" to send the message (button will be disabled until a message is entered)
- All connected users will see your message immediately
- Click "Disconnect" to leave the chat
- Change language using the language selector (English and Catalan available)
- Click "Show formatting options" to see available HTML formatting tags
The application uses an H2 in-memory database to store chat messages. You can access the H2 console to view and manage the database:
- Go to
http://localhost:8080/h2-console
- JDBC URL:
jdbc:h2:mem:chatdb
(for development environment) - Username:
sa
- Password:
password
The application uses Flyway for database schema migrations. This ensures that the database schema is always in a consistent state and allows for version-controlled database changes.
Migration files are located in src/main/resources/db/migration
and follow the naming convention V{version}__{description}.sql
. The current migrations include:
V1__Create_chat_messages_table.sql
: Creates the initial chat_messages table with appropriate indexesV2__Add_count_column_to_chat_messages.sql
: Adds a count column to the chat_messages table for user count messages
To add a new migration:
- Create a new SQL file in the
src/main/resources/db/migration
directory - Name it following the convention
V{next_version}__{description}.sql
- Write the SQL statements for your schema changes
- Restart the application - Flyway will automatically apply the new migration
The application uses externalized WebSocket configuration in the YAML files. This allows for easy modification of WebSocket settings without changing the code.
The following WebSocket properties can be configured in the application.yml file:
websocket:
endpoint: /ws/chat # WebSocket endpoint path
allowed-origins: "*" # Allowed origins for CORS (restrict in production)
max-text-message-size: 8192 # Maximum text message size in bytes
max-binary-message-size: 65536 # Maximum binary message size in bytes
max-session-idle-timeout: 600000 # Maximum session idle timeout in milliseconds
To customize WebSocket settings for different environments:
- Modify the appropriate YAML file (application.yml, application-docker.yml, etc.)
- Adjust the values as needed
- Restart the application for the changes to take effect
The application persists the following types of messages:
- Regular chat messages (CHAT)
- Join notifications (JOIN)
- Leave notifications (LEAVE)
System messages and user count updates are not persisted.
The application provides the following REST API endpoints for accessing chat message history:
GET /api/chat/messages
- Get all chat messagesGET /api/chat/messages/chat
- Get regular chat messagesGET /api/chat/messages/type/{type}
- Get messages by type (CHAT, JOIN, LEAVE, ERROR)GET /api/chat/messages/sender/{name}
- Get messages by sender name
The application supports multiple languages through client-side internationalization. Translation files are loaded directly from JSON files in the frontend, eliminating the need for backend API calls.
- English (en)
- Catalan (ca)
Translation files are located in src/main/resources/static/i18n/
as JSON files:
messages_en.json
- English translationsmessages_ca.json
- Catalan translations
To add a new language:
- Create a new JSON file in the
src/main/resources/static/i18n/
directory namedmessages_[language-code].json
- Copy the structure from an existing translation file
- Translate all the strings to the new language
- Add the language code to the
availableLocales
array ini18n.js
- Add a new option to the language selector in
index.html
if desired
The application uses a custom JavaScript internationalization implementation with the following features:
- Automatic language detection based on browser settings
- Manual language selection through UI
- Persistence of language preference in localStorage
- Support for message formatting with placeholders
- Support for plural forms
- Date and time formatting according to the selected locale
- Dynamic UI updates when changing language
The application includes several features to enhance the user experience:
- The "Connect" button is disabled until a valid name is entered
- The "Connect" button changes to "Disconnect" when connected
- The "Send" button is disabled until a message is entered
- Users can display formatting options by clicking the "Show formatting options" button
- Supported HTML tags include bold, italic, underline, highlight, and strikethrough
- The formatting options panel can be hidden by clicking the "Hide formatting options" button
- Errors are displayed directly in the chat for better visibility
- Connection errors are clearly indicated with appropriate messages
- XSS protection prevents malicious content from being displayed
- The number of online users is displayed with proper pluralization
- The count updates dynamically when users join or leave
- The display updates correctly when changing languages
This application is designed with a potential migration to microservices in mind. Key architectural decisions include:
-
Frontend/Backend Separation: The frontend loads translations directly from static JSON files, reducing coupling with the backend.
-
Externalized Configuration: WebSocket and database settings are externalized in YAML files, making it easier to configure different services.
-
Stateless Communication: The WebSocket communication is designed to be stateless, with message persistence handled separately.
-
Service Boundaries: The application is organized around clear service boundaries (chat, message persistence, etc.) that could become separate microservices.
-
Consistent Message Structure: The application uses a consistent message structure between frontend and backend, facilitating communication between different services.
-
Reduced Dependencies: The chat message handling logic has been simplified to reduce dependencies on external components like message utilities.
-
In-Memory Database: The application uses an in-memory H2 database for development, which can be easily replaced with a distributed database solution in a microservices architecture.
When migrating to microservices, consider the following potential services:
- Authentication Service
- Chat Message Service
- User Presence Service
- Message Persistence Service
- Notification Service
- Internationalization Service
The application includes robust XSS protection features to prevent malicious scripts from being executed. These features include:
- Input Validation: All user input is validated and sanitized to prevent malicious scripts from being injected.
- Output Encoding: All output is encoded to prevent malicious scripts from being executed.
- Content Security Policy (CSP): A CSP is implemented to define which sources of content are allowed to be executed within a web page.
- OWASP Java HTML Sanitizer: The OWASP Java HTML Sanitizer is used to sanitize user input and prevent malicious scripts from being injected.
i18n.js
: Client-side internationalization using modern JavaScript APIschat.js
: Frontend logic for WebSocket communication and UI interactionsindex.html
: User interface with internationalization attributes
ChatMessageHandler
: Handles WebSocket messagesChatMessageService
: Business logic for chat messagesChatMessageRepository
: Data access for chat messagesWebSocketConfig
: WebSocket configurationMessageValidator
: XSS protection and message validation
- Unit tests for backend components
- Integration tests for API endpoints
- Configuration tests for WebSocket setup
- HTML structure and attribute tests
Here are some ideas for extending the application:
- User authentication and authorization
- Private messaging between users
- Chat rooms/channels
- File sharing
- Message reactions/emojis
- Read receipts
- User typing indicators
- Message search functionality
- User profiles with avatars
- Push notifications for offline users
The application includes Docker support for easy containerization and deployment.
The easiest way to run the application is using Docker Compose:
# Start the application
docker-compose up -d
# Stop the application
docker-compose down
# Rebuild and start the application
docker-compose up -d --build
These commands will handle building the image and running the container with the appropriate configuration.
The Docker configuration includes:
- Multi-stage build for optimized image size
- Spring profile for Docker with specific configuration in
application-docker.yml
- Volume for H2 database (when using file-based H2 database)
When migrating to a microservices architecture, each service can have its own Dockerfile and be deployed independently. The current Docker configuration provides a good starting point for containerizing individual microservices.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.