This is a real-time interactive blackboard application that allows multiple users to draw simultaneously on a shared canvas. The application uses:
- Spring Boot for the backend server
- WebSockets for real-time communication
- React for the frontend components
- P5.js for canvas drawing functionality
- Redis for ticket-based authentication and session management
- Ticket-based Authentication for secure access control
Users must obtain a valid ticket from the server before they can draw on the canvas. Their drawings are instantly shared with all other authenticated users through WebSocket connections.
- BBAppStarter.java - Main Spring Boot application entry point with environment-based port configuration
- BBEndpoint.java - WebSocket endpoint that handles real-time communication between clients with ticket validation
- DrawingServiceController.java - REST controller providing server status and ticket generation
- BBConfigurator.java - WebSocket configuration
- RedisConfig.java - Redis configuration for cache and session management
- TicketRepository.java - Repository for managing authentication tickets using Redis
- BBApplicationContextAware.java - Spring context aware component for dependency injection in WebSocket endpoints
- index.html - Main HTML page that loads React, P5.js, and Babel
- bbComponents.jsx - React components including:
Editor
- Main layout componentBBCanvas
- Drawing canvas component with P5.js integrationWSBBChannel
- WebSocket communication class with ticket authentication- URL configuration functions for different service endpoints
- Ticket-based Authentication: Users must obtain valid tickets before accessing the drawing functionality
- Real-time Drawing: Draw on the canvas and see other users' drawings in real-time
- WebSocket Communication: Efficient real-time data exchange between authenticated clients
- Redis Integration: Persistent ticket management and session storage
- Responsive Design: Works on different screen sizes
- Environment Configuration: Automatically adapts to different deployment environments
- Secure Connections: Supports both WS (localhost) and WSS (production) protocols
- Java 8 or higher
- Maven 3.6 or higher
- Redis Server (6.0 or higher)
- Modern web browser with JavaScript enabled
For Windows users, I provide convenient batch scripts to manage Redis and the application:
setup-redis.bat
This script will:
- Check if Docker is installed
- Start a Redis container automatically
- Test the Redis connection
- Provide next steps
start-app.bat
This script will:
- Check if Redis is running
- Start Redis if needed
- Launch the Spring Boot application
- Open the application at http://localhost:8081
stop-app.bat
This script will:
- Stop the Redis container
- Stop any running Spring Boot processes
- Clean up resources
test-integration.bat
This comprehensive test script will:
- Verify Redis connectivity
- Compile the application
- Start all services
- Test all endpoints (health, status, ticket generation)
- Verify WebSocket availability
- Open the application in your browser
If you prefer manual setup or are using Linux/macOS:
-
Download Redis for Windows:
- Download from: https://github.com/MicrosoftArchive/redis/releases
- Or use Windows Subsystem for Linux (WSL) with Ubuntu
-
Using Chocolatey (Recommended):
choco install redis-64
-
Using WSL:
# Install WSL and Ubuntu wsl --install # In WSL terminal: sudo apt update sudo apt install redis-server # Start Redis sudo service redis-server start # Test Redis redis-cli ping
-
Manual Installation:
- Extract the downloaded ZIP file
- Run
redis-server.exe
from the command line - Default port: 6379
-
Ubuntu/Debian:
sudo apt update sudo apt install redis-server sudo systemctl start redis-server sudo systemctl enable redis-server
-
CentOS/RHEL:
sudo yum install redis sudo systemctl start redis sudo systemctl enable redis
-
macOS (using Homebrew):
brew install redis brew services start redis
# Test Redis connection
redis-cli ping
# Should return: PONG
# Check Redis status
redis-cli info server
-
Install Redis (see Redis Installation section above)
-
Clone the repository
git clone https://github.com/AnderssonProgramming/spring-sockets-rt-p5.git cd spring-sockets-rt-p5
-
Configure Redis (Optional - defaults work for local development) Edit
src/main/resources/application.properties
:# Redis configuration for BB Cache redis.bbcache.hostname=localhost redis.bbcache.port=6379 # Redis for session management spring.redis.host=localhost spring.redis.port=6379 # spring.redis.password=your_password # if password is required
-
Build the project
mvn clean compile
-
Install dependencies (if needed)
mvn dependency:copy-dependencies
Make sure Redis is running before starting the application:
# Windows (if installed manually)
redis-server.exe
# Linux/macOS
redis-server
# WSL (Ubuntu)
sudo service redis-server start
# Using Docker
docker run -d -p 6379:6379 redis:latest
mvn spring-boot:run
For Windows:
java -cp target/classes;target/dependency/* edu.eci.arsw.spring_sockets_rt_p5.BBAppStarter
For Linux/macOS:
java -cp target/classes:target/dependency/* edu.eci.arsw.spring_sockets_rt_p5.BBAppStarter
java -jar target/spring-sockets-rt-p5-1.0-SNAPSHOT.jar
- Client connects to the web application
- Canvas component loads and attempts to establish WebSocket connection
- Before drawing is allowed, the client must:
- Make a REST call to
/getticket
endpoint - Receive a unique ticket from the server
- Send this ticket as the first message in the WebSocket connection
- Make a REST call to
- Server validates the ticket using Redis storage
- If ticket is valid:
- Client is authenticated and can start drawing
- Drawing coordinates are broadcast to other authenticated clients
- If ticket is invalid:
- WebSocket connection is closed immediately
- Tickets are generated by the
TicketRepository
and stored in Redis - Each ticket is unique and can only be used once
- Tickets are automatically removed from Redis when validated
- Redis list operations ensure thread-safe ticket management
First, we start with a simple React component that renders a welcome message:
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<h1>Bienvenido</h1>
);
Then we extend it to create the main interface with the primary UI elements:
function Editor({name}) {
return (
<div>
<h1>Hello, {name}</h1>
<hr/>
<div id="toolstatus"></div>
<hr/>
<div id="container"></div>
<hr/>
<div id="info"></div>
</div>
);
}
We create a component to represent the drawing canvas:
function BBCanvas() {
const [svrStatus, setSvrStatus] = React.useState({loadingState: 'Loading Canvas ...'});
const myp5 = React.useRef(null);
const sketch = function (p) {
p.setup = function () {
p.createCanvas(700, 410);
}
p.draw = function () {
if (p.mouseIsPressed === true) {
p.fill(0, 0, 0);
p.ellipse(p.mouseX, p.mouseY, 20, 20);
}
if (p.mouseIsPressed === false) {
p.fill(255, 255, 255);
}
}
};
React.useEffect(() => {
myp5.current = new p5(sketch, 'container');
setSvrStatus({loadingState: 'Canvas Loaded'});
}, []);
return(
<div>
<h4>Drawing status: {svrStatus.loadingState}</h4>
</div>
);
}
We add WebSocket functionality with ticket-based authentication:
// Service URL configuration functions
function BBServiceURL() {
var url = WShostURL() + '/bbService';
console.log("BBService URL Calculada: " + url);
return url;
}
function ticketServiceURL() {
var url = RESThostURL() + '/getticket';
console.log("ticketService URL Calculada: " + url);
return url;
}
function WShostURL() {
var host = window.location.host;
var url = 'ws://' + (host);
console.log("host URL Calculada: " + url);
return url;
}
function RESThostURL() {
var host = window.location.host;
var protocol = window.location.protocol;
var url = protocol + '//' + (host);
console.log("host URL Calculada: " + url);
return url;
}
// Ticket retrieval function
async function getTicket() {
const response = await fetch(ticketServiceURL());
console.log("ticket: " + response);
return response;
}
// WebSocket communication class with authentication
class WSBBChannel {
constructor(URL, callback) {
this.URL = URL;
this.wsocket = new WebSocket(URL);
this.wsocket.onopen = (evt) => this.onOpen(evt);
this.wsocket.onmessage = (evt) => this.onMessage(evt);
this.wsocket.onerror = (evt) => this.onError(evt);
this.receivef = callback;
}
async onOpen(evt) {
console.log("In onOpen", evt);
var response = await getTicket();
var json;
if (response.ok) {
json = await response.json();
} else {
console.log("HTTP-Error: " + response.status);
}
this.wsocket.send(json.ticket);
}
onMessage(evt) {
console.log("In onMessage", evt);
if (evt.data != "Connection established.") {
this.receivef(evt.data);
}
}
onError(evt) {
console.error("In onError", evt);
}
send(x, y) {
let msg = '{ "x": ' + (x) + ', "y": ' + (y) + "}";
console.log("sending: ", msg);
this.wsocket.send(msg);
}
}
- Start Redis server (see Redis installation section)
- Start the application using one of the methods above
- Open your web browser
- Navigate to:
http://localhost:8081
(or the port shown in the console) - The application will automatically:
- Request a ticket from the server
- Authenticate the WebSocket connection
- Enable drawing functionality
- Start drawing on the canvas!
- Open the same URL in multiple browser tabs to test real-time collaboration
The application supports environment-based port configuration following the 12-factor app methodology:
- Default port: 8080 (when running locally)
- Environment port: Set the
PORT
environment variable for production deployments
Example:
export PORT=3000
java -jar target/spring-sockets-rt-p5-1.0-SNAPSHOT.jar
The application uses two separate Redis configurations:
-
Session Management (Spring Session):
spring.session.store-type=redis spring.session.redis.flush-mode=on_save spring.session.redis.namespace=blackboard:session spring.redis.host=localhost spring.redis.port=6379
-
BB Cache (Ticket Management):
redis.bbcache.hostname=localhost redis.bbcache.port=6379
The application automatically detects the environment and configures the appropriate WebSocket protocol:
- Localhost: Uses
ws://
(unsecured WebSocket) - Production: Uses
wss://
(secured WebSocket)
GET /status
- Returns server status and current timestampGET /getticket
- Returns a unique authentication ticket for WebSocket accessGET /health
- Returns application health status including Redis connectivity
/bbService
- Main WebSocket endpoint for real-time drawing communication (requires ticket authentication)
- Client Connection: User loads the web page and the React application initializes
- Ticket Request: Canvas component requests an authentication ticket via REST API (
/getticket
) - WebSocket Connection: Client establishes WebSocket connection to
/bbService
- Authentication: First message sent through WebSocket is the authentication ticket
- Ticket Validation: Server validates ticket using Redis and either accepts or rejects the connection
- Drawing Events: Authenticated users can draw, coordinates are sent via WebSocket
- Broadcasting: Server broadcasts drawing coordinates to all other authenticated clients
- Real-time Rendering: All authenticated clients receive and render drawing coordinates
src/
├── main/
│ ├── java/edu/eci/arsw/spring_sockets_rt_p5/
│ │ ├── BBAppStarter.java # Main application class
│ │ ├── BBApplicationContextAware.java # Spring context provider
│ │ ├── configurator/
│ │ │ ├── BBConfigurator.java # WebSocket configuration
│ │ │ └── RedisConfig.java # Redis configuration
│ │ ├── controllers/
│ │ │ └── DrawingServiceController.java # REST controller (status, tickets)
│ │ ├── endpoints/
│ │ │ └── BBEndpoint.java # WebSocket endpoint with authentication
│ │ └── repositories/
│ │ └── TicketRepository.java # Redis-based ticket management
│ └── resources/
│ ├── application.properties # Spring and Redis configuration
│ └── static/
│ ├── index.html # Main HTML page
│ └── js/
│ └── bbComponents.jsx # React components with authentication
└── test/
└── java/edu/eci/arsw/spring_sockets_rt_p5/
└── AppTest.java # Unit tests
- Spring Boot 3.1.1 - Backend framework
- Spring WebSocket 6.0.10 - Real-time communication
- Spring Data Redis 3.1.1 - Redis integration
- Spring Session 3.1.1 - Session management with Redis
- Redis - In-memory data store for tickets and sessions
- React 18 - Frontend library
- P5.js 0.7.1 - Canvas drawing library
- Babel Standalone - JSX transformation
- Maven - Build tool
This application is ready for deployment on cloud platforms. For production deployment:
- Use a managed Redis service (AWS ElastiCache, Azure Redis Cache, etc.)
- Or deploy Redis instance on your cloud platform
- Update connection properties for your Redis instance
-
Set environment variables:
export PORT=8080 export REDIS_HOST=your-redis-host export REDIS_PORT=6379 export REDIS_PASSWORD=your-redis-password # if required
-
Update application.properties for production:
spring.redis.host=${REDIS_HOST:localhost} spring.redis.port=${REDIS_PORT:6379} spring.redis.password=${REDIS_PASSWORD:} redis.bbcache.hostname=${REDIS_HOST:localhost} redis.bbcache.port=${REDIS_PORT:6379}
-
Redis Connection Failed
Error: Cannot connect to Redis at localhost:6379
Solution:
- Ensure Redis server is running
- Check Redis configuration in application.properties
- Verify Redis is accessible on the specified host/port
-
WebSocket Authentication Failed
WebSocket connection closed immediately
Solution:
- Check browser console for ticket request errors
- Verify
/getticket
endpoint is accessible - Ensure Redis is storing tickets correctly
-
Canvas Not Loading
- Check browser console for JavaScript errors
- Ensure all CDN resources are accessible
- Verify React and P5.js libraries are loaded
-
Port Already in Use
- Change the port using environment variable:
export PORT=9090
- Or stop the process using the port:
netstat -ano | findstr :8081
- Change the port using environment variable:
-
Compilation Errors
- Ensure all Maven dependencies are properly installed
- Run
mvn clean compile
to rebuild the project - Check that Java version is compatible (Java 8+)
-
Check Redis Status:
redis-cli ping # Should return PONG redis-cli info server
-
View Redis Logs:
# Linux/macOS tail -f /var/log/redis/redis-server.log # Check Redis configuration redis-cli CONFIG GET "*"
-
Test Ticket Storage:
# Connect to Redis CLI redis-cli # Check if tickets are being stored LLEN ticketStore LRANGE ticketStore 0 -1
The project includes the following key dependencies for Redis integration:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
- Start the application with Redis running
- Open multiple browser tabs/windows
- Each tab will:
- Automatically request a unique ticket
- Authenticate with the WebSocket endpoint
- Allow drawing that syncs across all authenticated sessions
# Monitor Redis commands in real-time
redis-cli monitor
# Check ticket storage
redis-cli LLEN ticketStore
redis-cli LRANGE ticketStore 0 -1
- Fork the repository
- Create a feature branch
- Make your changes
- Test with Redis running
- Submit a pull request
- Advanced Authentication: Integration with OAuth2 or JWT tokens
- User Management: Named users and persistent user sessions
- Drawing Tools: Brush size, colors, shapes selection
- Room-based Collaboration: Separate drawing rooms with different access controls
- Drawing Persistence: Save and load drawings from Redis or database
- Mobile Optimization: Touch support optimization for mobile devices
- Drawing History: Undo/redo functionality with Redis-based state management
- Real-time Chat: Text chat alongside drawing collaboration
- Performance Monitoring: Redis metrics and WebSocket connection monitoring