A secure, scalable, and horizontally distributed Real-Time Notice Broadcasting system built with Java 17, Spring Boot 3, and WebSockets (STOMP).
This platform fulfills the strict requirement of zero persistence for messages:
- Like a live sports broadcast, only currently connected users receive messages.
- Offline users do not receive missed messages upon reconnecting.
- Users and Departments are persisted in PostgreSQL.
- Notices strictly pass-through via memory and Redis.
- Backend: Java 17, Spring Boot 3.2.x
- Security: Spring Security 6, JWT (io.jsonwebtoken 0.11.5)
- Database: PostgreSQL (Hibernate & Spring Data JPA)
- High Availability (WebSocket Scaling): Redis PubSub
- Build Tool: Maven
Provides stateless REST API security. The JWT is validated in the REST Filter chain, as well as a specialized JwtChannelInterceptor for STOMP CONNECT frames.
Identity Standardization: To ensure private messaging works reliably, the WebSocket Principal identity is mapped strictly to the user's email literal string.
Standard STOMP relies on in-memory mapping. When scaled to Multiple Nodes, a user connected to Node A wouldn't receive a broadcast from Node B. This system utilizes Redis Pub/Sub to solve this:
- Notice Topic (
notice_broadcast_topic): Every broadcast is published here. All nodes listen and push to their local connected users. - ACK Topic (
notice_ack_topic): Every view acknowledgement (ACK) from a client is published here. This allows view count synchronization across the entire cluster.
This platform implements a real-time view counting feature with zero persistence:
- When a client receives a message, they send a
ViewAckvia STOMP to/app/view. - The server publishes this ACK to Redis.
- The
ViewServicededuplicates viewers in-memory (usingConcurrentHashMap). - Updated counts are streamed back to the original sender's private queue (
/user/queue/view-count).
- Roles:
ADMIN,TEACHER,STUDENT. - Departments:
MATHS,PHYSICS,COMPUTER_SCIENCE. - Identity: Users have a
BIGINT(Long) primary key for REST operations and aUUIDfor secondary identification. Email remains the unique login identifier.
/topic/live/all(Global) -> OnlyADMINcan send here./topic/live/department/{dept}(Departmental) ->ADMINorTEACHER(only to their own department)./user/queue/live(Private) -> Users receive their own private messages here./user/queue/view-count(View Updates) -> Broadcasters receive live 👁️ updates here.
- Java 17+
- PostgreSQL listening at
localhost:5432with anoticeboarddatabase (User/Pass:postgres/191204). - Redis running on
localhost:6379.
You can override standard variables in application.yml via environment:
DB_URL,DB_USER,DB_PASSWORDREDIS_HOST,REDIS_PORT,REDIS_PASSWORDJWT_SECRET,JWT_EXPIRATION_MS
mvn clean install
mvn spring-boot:runThe server starts on port 8081. Access the UI at: http://localhost:8081/login.html
The platform includes a built-in Vanilla HTML/JS dashboard:
- Login (
/login.html): Authenticate with email and password. - Signup (
/signup.html): Create a new account with a specific Role and Department. - Home (
/dashboard.html):- Student View: Read-only panels for Global, Departmental, and Private notices.
- Admin/Teacher View: Panel to author broadcasts and a "Sent Messages" panel with live View Count (👁️) updates.
- Signup/Login: Go to
/login.htmland create a Teacher account. - Student View: Open an incognito window and create a Student account (same department).
- Broadcast: As the Teacher, send a "Department" notice.
- Verify:
- The Student receives the message instantly.
- The Teacher's dashboard shows
👁️ 1immediately after the Student views it.
Login Endpoint: POST /api/auth/login
{ "email": "admin@school.com", "password": "password123" }Response: Returns token and id (BIGINT).
Broadcasting:
| Action | Endpoint | Auth |
|---|---|---|
| Global | POST /api/notices/broadcast/all |
ADMIN |
| Private | POST /api/notices/private/{id} |
ADMIN/TEACHER |
Connect URL: ws://localhost:8081/ws-noticeboard (Requires Authorization: Bearer <token>)
Subscriptions:
/topic/live/all/topic/live/department/{DEPT}/user/queue/live/user/queue/view-count(Broadcasters only)