University of Balamand – Faculty of Arts & Sciences
CSIS 231 – Java Technology
Secure online learning platform with Spring Boot + PostgreSQL + JavaFX 2D/3D,
JWT + OTP 2FA, and role-based dashboards (Admin / Instructor / Student).
LearnOnline is a modular e-learning platform built as the final project for CSIS 231 – Java Technology / Advanced Java.
The system provides:
- A secure REST backend (Spring Boot + Spring Security + JWT + OTP 2FA).
- A desktop JavaFX client with:
- Modular FXML views and shared styling (
styles.css). - 2D dashboards using JavaFX charts (
BarChart,LineChart). - 3D analytics using JavaFX 3D (
SubScene,Box,PerspectiveCamera,RotateTransition).
- Modular FXML views and shared styling (
- A PostgreSQL database as the single source of truth for all business logic and persistence.
- Student
- Register and login (with OTP-based 2FA when required).
- Browse courses, enroll, and take quizzes.
- View recent quiz performance and progress in 2D and 3D visualizations.
- Instructor
- Manage their courses and quizzes.
- View enrollments and quiz statistics.
- Open 3D analytics for their courses and see aggregated quiz results.
- Admin
- Global view and management of users, roles, categories, courses, enrollments.
- Access data visualizations (2D + 3D) for any instructor’s courses via a course selector.
This project is explicitly designed to match the CSIS 231 Final Project requirements (see section 11) and to demonstrate:
- Real JavaFX 2D + 3D graphics for dashboards and analytics.
- JWT + OTP-based security, with OTP used both for:
- Two-Factor Authentication (2FA) on login (
LOGIN_2FA). - Forgot-password flows (
PASSWORD_RESETOTP).
- Two-Factor Authentication (2FA) on login (
Monorepo structure:
LearnOnline/
├── learnonline-api/ # Spring Boot backend (REST API + security)
└── learnonline-desktop/ # JavaFX 17 client (FXML + 2D/3D visualizations)
- Layered architecture:
controller– REST endpoints.service– business logic, validation, security checks.repository– Spring Data JPA repositories.domain/model– entities (User, Role, Course, Enrollment, Quiz, OTP, etc.).exception– custom exception hierarchy.common– shared utilities, DTOs,GlobalExceptionHandler, constants.
- Stateless security with JWT.
- Email-based OTP for:
- LOGIN_2FA (optional 2-factor on login).
- PASSWORD_RESET (forgot-password).
- Centralized error handling via
@ControllerAdvice.
- Stores:
- Users & Roles
- Categories & Courses
- Enrollments
- Quizzes, Questions, Results
- OTP tokens (purpose + expiry)
- Uses Spring Data JPA for ORM.
- Enforces relationships such as:
User – Enrollment – CourseCourse – Quiz – Result.
- JavaFX 17 application with:
- FXML views under
learnonline-desktop/src/main/resources/com/example/demo. - Controllers under
learnonline-desktop/src/main/java/com/example/demo. - Shared stylesheet:
styles.css(dark theme, buttons, cards, tabs, 2D/3D viz styles).
- FXML views under
- Navigation:
Launcher+HelloApplicationhandle scene switching and role-based routing.- Dedicated controllers for Admin, Instructor, Student, and Graphics (2D/3D).
- Java 17+
- Spring Boot (Web, Security, Validation)
- Spring Data JPA / Hibernate
- Spring Mail / JavaMail (for OTP e-mails)
- Spring Security + JWT
- Jakarta Bean Validation (
jakarta.validation)
- PostgreSQL
- Configuration via
application.yml+ environment variables.
- JavaFX 17 (controls, FXML, 2D + 3D)
- JavaFX charts:
BarChart<String, Number>LineChart<String, Number>
- JavaFX 3D:
SubScene,Group,Box,PhongMaterial,PerspectiveCamera- Animations using
RotateTransition
- Shared CSS (
styles.css) with:- Buttons:
.primary-button,.secondary-button,.ghost-pill, … - Layout helpers: cards, sections, chips, toolbars
- Styled
TabPaneand chart backgrounds
- Buttons:
- Maven (multi-module)
- IntelliJ IDEA
- pgAdmin (for DB management)
- Register / login with JWT auth and optional OTP 2FA.
- Browse available courses and enroll.
- Take quizzes and see their results.
- Open a “Visualize Progress” screen with:
- 3D bars for quiz scores using JavaFX 3D.
- 2D charts (Bar/Line) for recent quiz performance by quiz name.
- Manage owned courses (create, update, archive).
- Manage quizzes attached to their courses.
- View enrollments & quiz statistics.
- Open “3D Analytics”:
- 3D analytics per course (enrollments, scores).
- 2D chart: average quiz score per quiz for the selected course.
- Manage:
- Users & roles
- Categories
- Courses
- Enrollments
- Open “Data Visualizations”:
- Select any instructor’s course from a ComboBox.
- See 3D enrollment bar(s) for that course.
- See 2D quiz averages by quiz name.
- User sends
POST /api/auth/loginwith username/password. - Backend validates credentials:
- If 2FA not required → returns
AuthResponsewith JWT. - If 2FA required → throws
OtpRequiredException, and:- Returns HTTP 202 Accepted with
otpRequired = true. - Sends LOGIN_2FA OTP to the user’s e-mail.
- Returns HTTP 202 Accepted with
- If 2FA not required → returns
- JavaFX client opens
otp.fxmland callsPOST /api/auth/otp/verify. - Backend verifies OTP:
- On success → returns normal
AuthResponsewith JWT. - On failure →
401 Unauthorizedwith unified error JSON.
- On success → returns normal
- Client stores JWT in TokenStore / SessionStore and includes it in
Authorization: Bearer <jwt>for all calls.
- User clicks “Forgot password?” in the JavaFX client.
- Client calls
POST /api/auth/password/forgotwith e-mail. - Backend sends a PASSWORD_RESET OTP to this e-mail.
- User enters OTP + new password; client calls
POST /api/auth/password/reset. - Backend validates OTP and updates the password.
POST /api/auth/otp/verify
Verify OTP for both LOGIN_2FA and PASSWORD_RESET.POST /api/auth/otp/request
Resend OTP when the user did not receive it or it expired.
- Role-based access enforced via Spring Security.
- UI hides/disables actions that are not allowed for Student/Instructor/Admin.
GlobalExceptionHandlerconverts exceptions into a unifiedErrorResponsewith:- timestamp
- HTTP status
- error code
- message
- validation details (if any)
Location:
learnonline-desktop/src/main/java/com/example/demo/graphics/GraphicsPlaygroundController.javalearnonline-desktop/src/main/resources/com/example/demo/graphics/graphics_playground.fxml
- Top bar
- “↩ Back” ghost-pill button routing back to the correct dashboard (Student/Instructor/Admin).
- Title + chips for selected course, last refresh, role.
- TabPane with two tabs:
- 3D Analytics
SubScenecontaining a 3DGroupofBoxbars.PerspectiveCameraand simple materials (PhongMaterial).RotateTransitionfor continuous rotation / highlighting.- Metrics:
- Enrollments per course (Instructor/Admin).
- Student quiz scores in 3D (Student view).
- 2D Progress
BarChart<String, Number>orLineChart<String, Number>inside a “viz card”.- For students: recent quiz results (quiz name vs score).
- For instructors/admin: average scores per quiz for the selected course.
- 3D Analytics
GET /api/student/dashboard
Student courses & recent quiz results.GET /api/instructor/dashboard
Instructor courses + stats.GET /api/instructors/{userId}/courses
Course selector for admin/instructor.GET /api/courses/{courseId}/enrollments
Enrollment count per course → 3D bars.GET /api/statistics/courses/{courseId}/quiz-averages
Average quiz scores → 2D charts.
learnonline-api/
├── src/main/java/com/csis231/api
│ ├── auth/ # Login, register, forgot/reset password, JWT
│ ├── otp/ # OTP verify/request endpoints
│ ├── user/ # User & Role domain, services, controllers
│ ├── category/ # Course categories
│ ├── course/ # Courses & their details
│ ├── coursematerial/ # Course materials
│ ├── enrollment/ # Enrollments
│ ├── quiz/ # Quizzes, questions, results
│ ├── dashboard/ # Student & Instructor dashboards
│ ├── statistics/ # Quiz averages & analytics
│ ├── common/ # DTOs, helpers, GlobalExceptionHandler, constants
│ └── exception/ # Custom exception hierarchy
└── src/main/resources
└── application.yml
learnonline-desktop/
├── src/main/java/com/example/demo
│ ├── auth/ # Login, register, forgot password, OTP, reset controllers
│ ├── common/ # ApiClient, ApiException, ErrorDialog, AlertUtils, SessionStore, TokenStore
│ ├── course/ # Course catalog, details, editor, enrollments
│ ├── dashboard/ # Admin dashboard controller
│ ├── instructor/ # InstructorDashboardController
│ ├── student/ # StudentDashboardController
│ ├── quiz/ # Quiz creation + quiz-taker screens
│ ├── graphics/ # GraphicsPlaygroundController (2D/3D visualizations)
│ ├── Launcher.java # Central navigation helper
│ └── HelloApplication.java # JavaFX entry point
└── src/main/resources/com/example/demo
├── fxml/ # login.fxml, register.fxml, otp.fxml, forgot_password.fxml, dashboard.fxml, ...
├── graphics/ # graphics_playground.fxml
├── styles.css # shared JavaFX CSS
└── icons/ # images/icons (if any)
Below are the main mappings from:
AuthControllerOtpControllerUserControllerCategoryControllerCourseControllerCourseMaterialControllerEnrollmentControllerDashboardControllerStatisticsControllerQuizController
| HTTP | Path | Description |
|---|---|---|
| POST | /api/auth/login |
Login with username/password (with optional 2FA). |
| POST | /api/auth/register |
Register a new user. |
| POST | /api/auth/password/forgot |
Start forgot-password OTP flow. |
| POST | /api/auth/password/reset |
Reset password using a PASSWORD_RESET OTP. |
| HTTP | Path | Description |
|---|---|---|
| POST | /api/auth/otp/verify |
Verify OTP (LOGIN_2FA or PASSWORD_RESET). |
| POST | /api/auth/otp/request |
Resend OTP for a given username. |
| HTTP | Path | Description |
|---|---|---|
| GET | /api/csis-users |
List users (paged). |
| GET | /api/csis-users/{id} |
Get a single user by id. |
| POST | /api/csis-users |
Create a new user. |
| PUT | /api/csis-users/{id} |
Update an existing user. |
| DELETE | /api/csis-users/{id} |
Delete a user. |
| GET | /api/csis-users/me |
Get the authenticated user profile. |
| HTTP | Path | Description |
|---|---|---|
| GET | /api/categories |
List all categories. |
| GET | /api/categories/{id} |
Get category by id. |
| POST | /api/categories |
Create category. |
| PUT | /api/categories/{id} |
Update category. |
| DELETE | /api/categories/{id} |
Delete category. |
| HTTP | Path | Description |
|---|---|---|
| GET | /api/courses |
List courses (with filters/paging). |
| GET | /api/courses/{id} |
Get course details by id. |
| POST | /api/courses |
Create a new course. |
| PUT | /api/courses/{id} |
Update an existing course. |
| DELETE | /api/courses/{id} |
Delete/archive a course. |
| HTTP | Path | Description |
|---|---|---|
| GET | /api/courses/{courseId}/materials |
List materials for a course. |
| POST | /api/courses/{courseId}/materials |
Add a new material to a course. |
| DELETE | /api/materials/{id} |
Delete a material by id. |
| HTTP | Path | Description |
|---|---|---|
| POST | /api/enrollments/enroll |
Enroll a student in a course. |
| GET | /api/students/{userId}/enrollments |
List a student’s enrollments. |
| GET | /api/courses/{courseId}/enrollments |
List enrollments for a course (for stats/viz). |
| HTTP | Path | Description |
|---|---|---|
| GET | /api/student/dashboard |
Student dashboard data. |
| GET | /api/instructor/dashboard |
Instructor dashboard data. |
| GET | /api/instructors/{userId}/courses |
Courses for a specific instructor. |
| HTTP | Path | Description |
|---|---|---|
| GET | /api/statistics/courses/{courseId}/quiz-averages |
Average score per quiz for a given course. |
| HTTP | Path | Description |
|---|---|---|
| POST | /api/quizzes |
Create a new quiz. |
| POST | /api/quizzes/{quizId}/questions |
Add questions to a quiz. |
| GET | /api/quizzes/{quizId} |
Get quiz definition (questions). |
| POST | /api/quizzes/{quizId}/submit |
Submit answers to a quiz. |
| GET | /api/quizzes/{quizId}/results |
Aggregated results for the quiz (instructor/admin). |
| GET | /api/quizzes/{quizId}/my-result |
Current user’s result for that quiz. |
| DELETE | /api/quizzes/{quizId} |
Delete a quiz. |
- JDK 17+
- Maven 3+
- PostgreSQL (local or Docker)
git clone https://github.com/M677871/LearnOnline.git
cd LearnOnlineCREATE DATABASE csis_231_db;Create/edit: learnonline-api/src/main/resources/application.yml:
server:
port: 8080
spring:
datasource:
url: ${DB_URL:jdbc:postgresql://localhost:5432/csis_231_db}
username: ${DB_USER:postgres}
password: ${DB_PASS:postgres}
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate.format_sql: true
mail:
host: ${MAIL_HOST:smtp.gmail.com}
port: ${MAIL_PORT:587}
username: ${MAIL_USERNAME:your-gmail@example.com}
password: ${MAIL_PASSWORD:your-gmail-app-password} # Use a Gmail App Password
properties:
mail.smtp.auth: true
mail.smtp.starttls.enable: true
mail.smtp.starttls.required: true
mail.smtp.auth.mechanisms: LOGIN PLAIN
mail.smtp.ssl.trust: smtp.gmail.com
mail.smtp.connectiontimeout: 10000
mail.smtp.timeout: 10000
mail.smtp.writetimeout: 10000
mail.debug: false
management:
health:
mail:
enabled: false
jwt:
secret: ${JWT_SECRET:ChangeThisSecretForProductionUseALongRandomString}
expiration: ${JWT_EXPIRATION:900000} # 15 minutes
mail:
from: ${MAIL_FROM:your-gmail@example.com}Important: never commit real passwords or secrets. Use environment variables in production.
DB_URL,DB_USER,DB_PASSMAIL_HOST,MAIL_PORT,MAIL_USERNAME,MAIL_PASSWORD,MAIL_FROMJWT_SECRET,JWT_EXPIRATION
From learnonline-api:
mvn clean install
mvn spring-boot:runBackend will start on http://localhost:8080 (unless overridden).
Files: learnonline-api/Dockerfile, learnonline-api/docker-compose.yml, learnonline-api/.dockerignore.
Quick run (recommended when you already build the JAR on host):
cd C:\dev\csis_231_project\learnonline-api
# 1) Build executable JAR (host)
.\mvnw.cmd clean package -DskipTests
# 2) Build Docker images
docker compose build
# 3) Start API + PostgreSQL in background
docker compose up -d
# 4) Check logs (follow)
docker compose logs -f --tail=100
# 5) Stop containers
docker compose down
# 6) Stop and remove DB data volume (optional)
docker compose down -v- The Compose file creates two services:
appandpostgres. - App container exposes
8080. - PostgreSQL container exposes
5432. - PostgreSQL credentials are set by Compose:
POSTGRES_DB=learnonlinePOSTGRES_USER=postgresPOSTGRES_PASSWORD=postgres
- App uses environment variables for DB connection:
SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/learnonlineSPRING_DATASOURCE_USERNAME=postgresSPRING_DATASOURCE_PASSWORD=postgres
- Compose includes a
healthcheckfor Postgres, andappdepends onpostgreswithservice_healthyso the API waits for DB readiness.
How containers communicate:
- Docker Compose creates a private network.
- The app connects to PostgreSQL using the service name
postgresas host. - Data is persisted in volume
postgres_data.
Use:
http://HOST_IP:8080
Replace HOST_IP with the IP of the machine running Docker. On Windows, you can get it with:
ipconfigIf needed, allow inbound TCP 8080 in the host firewall.
-
If the app fails with
Unable to access jarfile /app/app.jar:- Build the JAR first:
.\mvnw.cmd clean package -DskipTests. - Verify it exists:
dir target\*.jar. - Ensure
.dockerignoreallows jar files intarget(target/*and!target/*.jar). - Rebuild without cache:
docker compose build --no-cache.
- Build the JAR first:
-
Useful checks:
docker compose psto view service status.docker compose logs -fto stream logs.docker exec -it learnonline-app shto inspect app container.
From learnonline-desktop:
mvn clean install
mvn javafx:runOr run HelloApplication from your IDE.
The JavaFX app will handle login (with OTP 2FA), dashboards, and the 2D/3D analytics playground.
- SessionStore / TokenStore track:
- Current user
- JWT token(s)
- Role
- All HTTP calls go through a shared ApiClient:
- Adds
Authorization: Bearer <jwt>header. - Deserializes JSON into DTOs.
- Wraps errors in
ApiException.
- Adds
- Errors are displayed using:
ErrorDialog.showError(ex)orAlertUtils.showError(...)
- Async calls use
CompletableFuture+Platform.runLaterto keep UI responsive.
- DTOs annotated with
@NotBlank,@Email,@Size, etc. - Validation errors are handled in
GlobalExceptionHandlerand returned in a structured JSON format that the client shows nicely.
Spring Boot Backend
- Proper layered architecture (Controller / Service / Repository / Domain).
- PostgreSQL schema mapped via JPA entities.
- Business logic pushed into service layer, not controllers.
- Full, documented REST API (see section 8).
Advanced Features & JavaFX 2D/3D
- Real 3D graphics:
- JavaFX 3D (
SubScene,Box,PerspectiveCamera,RotateTransition). - 3D bars for course enrollments and student quiz scores.
- JavaFX 3D (
- 2D charts:
BarChart/LineChartfor quiz performance and averages.
- Visualizations fed from real backend endpoints:
/api/student/dashboard/api/instructor/dashboard/api/courses/{courseId}/enrollments/api/statistics/courses/{courseId}/quiz-averages
JavaFX UI
- Multiple FXML screens: login, register, forgot password, OTP, dashboards, courses, quizzes, graphics.
- Shared
styles.csswith reusable style classes. - Clear role-based dashboards (Student / Instructor / Admin).
- Dedicated 2D/3D visualization playground with back navigation and course selector.
Security, OTP 2FA & Forgot Password
- JWT + Spring Security for authentication and authorization.
- OTP 2FA on login via
/api/auth/login+/api/auth/otp/verify. - OTP-based forgot-password via
/api/auth/password/forgot+/api/auth/password/reset. - Centralized error handling and validation.
Documentation & API Docs
- This README includes:
- Setup and configuration via
application.yml. - Architecture & role-based feature overview.
- Detailed description of 2D/3D visualizations.
- Full REST API reference.
- Explanation of OTP 2FA and forgot-password OTP flows.
- Setup and configuration via
- Code is structured and ready for Javadoc / Swagger (springdoc) if extended.
- Add springdoc-openapi for Swagger UI.
- Add WebSockets for real-time quiz/enrollment updates.
- More analytics (engagement, retention, completion rate).
- Multi-metric 3D scenes (e.g., 2–3 bars per course).
- Audit logging and admin activity reports.
Author: Miled Issa
Course: CSIS 231 – Java Technology
Institution: University of Balamand – Faculty of Arts & Sciences