A JavaFX desktop application for managing university academic operations: student/lecturer/course management, enrollment, and user authentication with OTP-based password recovery.
- Java 21+ (required for JavaFX 25.0.2)
- JavaFX SDK 25.0.2 (path hardcoded in shell scripts:
/home/saifdemos/programs/javafx-sdk-25.0.2/lib— update scripts if your installation differs) - PostgreSQL 12+ running at
127.0.0.1:5432 - PostgreSQL JDBC Driver
postgresql-42.7.10.jar(co-located with JavaFX SDK lib dir)
| Directory | Description |
|---|---|
scenes/ |
All JavaFX scene classes (UI screens) |
classes/ |
Data model classes |
utility/ |
Utility/helper classes |
images/ |
UI assets (icons, backgrounds) |
fonts/ |
Custom fonts (Poppins, Bebas Neue) |
| Class | File | Purpose |
|---|---|---|
Person |
classes/Person.java |
Abstract base class for people |
Student |
classes/Student.java |
Student model, extends Person |
Lecturer |
classes/Lecturer.java |
Lecturer model, extends Person |
Course |
classes/Course.java |
Course model with lecturer assignment |
Collage |
classes/Collage.java |
College data (name, dean, departments) |
Department |
classes/Department.java |
Department model (name + code) |
DBUtil |
utility/DBUtil.java |
Database connection helper |
Assistor |
utility/Assistor.java |
Alert dialogs and UI helpers |
classes/Person.java — Abstract class with abstract method:
public abstract class Person {
private final SimpleStringProperty name;
private final SimpleLongProperty id;
public Person(String name, long id) { ... }
public abstract String getRoleDescription(); // ← abstract method
@Override
public String toString() { // ← override from Object
return "Role: " + getRoleDescription() + "\nName: " + getName() + "\nID: " + getId();
}
}Implemented in subclasses:
Student.java:28→return "Student";Lecturer.java:26→return "Lecturer";
Student and Lecturer both extend Person and override getRoleDescription():
// classes/Student.java
public class Student extends Person {
@Override
public String getRoleDescription() { return "Student"; }
}
// classes/Lecturer.java
public class Lecturer extends Person {
@Override
public String getRoleDescription() { return "Lecturer"; }
}Department also has its own toString() override:
// classes/Department.java:12-15
@Override
public String toString() {
return name + " (" + code + ")";
}utility/Assistor.java :27 — createWithBackground is overloaded (same name, different parameters):
// Version 1: uses default dimensions (815 x 665)
public static StackPane createWithBackground(Node content, double opacity) {
return createWithBackground(content, opacity, DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
// Version 2: custom dimensions
public static StackPane createWithBackground(Node content, double opacity, double width, double height) {
...
}utility/SceneAnimator.java :78 — fadeIn is overloaded:
public static void fadeIn(Node node) { ... } // default duration
public static void fadeIn(Node node, Duration duration) { ... } // custom durationAssociation (uses relationship):
Student↔CourseviaSTUDENT_COURSEjunction table (many-to-many)Lecturer↔CourseviaCOURSE_LECTURERjunction table (many-to-many)
Aggregation (has-a, lifetime independent):
// classes/Collage.java:14,22-32
private List<Department> departments; // College HAS departments, but departments can exist independently
public Collage() {
this.departments = new ArrayList<>();
this.departments.add(new Department("Physics Department", 101));
// ...
}Composition (owns-a, lifetime dependent — owned object dies with owner):
// classes/Student.java:16,23
private final ArrayList<Course> enrolledCourses; // Exists only within Student
public Student(...) {
this.enrolledCourses = new ArrayList<>();
}ArrayLists are used throughout the model classes:
// classes/Student.java:16
private final ArrayList<Course> enrolledCourses;
// classes/Lecturer.java:13
private final ArrayList<Course> coursesTeaching;
// classes/Course.java:11
private final ArrayList<Lecturer> assignedLecturers;
// classes/Collage.java:14
private List<Department> departments; // ArrayList internallyManaged via methods like enrollInCourse(Course course) and assignLecturer(Lecturer lecturer).
Try-catch blocks are used extensively for database operations:
// utility/DBUtil.java:14-32
public static Connection dbConnect() {
try {
Class.forName("org.postgresql.Driver");
con = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
} catch (ClassNotFoundException e) {
System.err.println("PostgreSQL JDBC Driver not found.");
} catch (SQLException ex) {
System.err.println("Failed to connect to the database.");
}
return con;
}Background tasks use Task<> with setOnFailed handlers (LogIn.java:247-261, LogIn.java:342-367).
- No FXML: All UI built programmatically in Java code
- Scenes (in
scenes/):HomePage,LogIn,SignUp,Dashboard,ManageStudents,ManageLecturer,ManageCourses,EnrollStudent,EnrollLecturer,StudentProfile,AccountInfo,About,SMSApp - Password visibility toggle: dual
PasswordField+TextFieldwithsetVisible()/setManaged()swap, wrapped inHBoxwith toggle button (LogIn.java:40-56,SignUp.java:77-93,scenes/AccountInfo.java:122-140) - Custom fonts: Poppins loaded via
Font.loadFont()inMainApp.java:19 - Alerts: Standardized via
utility/Assistor.java(Success uses AcademiX icon, Warning/Error do not)
- Connection:
utility/DBUtil.java— staticdbConnect()method with hardcoded credentials - Tables:
STUDENT(student_id, name, email, password, phone, gender, birth_date, cgpa, major, minor)LECTURER(lecturer_id, name, phone, birth_date, gender, salary)COURSE(course_id, name, record_hours)STUDENT_COURSE(junction table)COURSE_LECTURER(junction table)ADMINS(admin_id, email, password)
- Delete constraint: Junction tables must be cleaned before deleting main records (see
ManageLecturer.java:597-611,ManageCourses.java:271-280,ManageStudents.java:667-681) - Auto-email generation: On signup, email is auto-generated as
firstName.studentId@academix.edu(SignUp.java:213-220)
The OTP password reset feature uses Java Socket programming for client-server communication between the main app and the SMS App.
-
SMS Server (
scenes/SMSApp.java):- Standalone JavaFX
Applicationthat opens aServerSocketon port12345. - Runs a background loop accepting client connections and reading OTP messages.
- Displays received messages in a
ListViewUI.
Key code:
// scenes/SMSApp.java:27-29, 111-128 public class SMSApp extends Application { private ServerSocket serverSocket; @Override public void start(Stage stage) { new Thread(() -> { try (ServerSocket server = new ServerSocket(12345)) { serverSocket = server; while (!serverSocket.isClosed()) { Socket clientSocket = server.accept(); BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String message = in.readLine(); if (message != null) { Platform.runLater(() -> messages.add(0, message)); } in.close(); clientSocket.close(); } } catch (IOException e) { e.printStackTrace(); } }).start(); } }
- Standalone JavaFX
-
SMS Client (
scenes/LogIn.javaForgot Password flow):- When an OTP is generated, a new thread creates a
Socketconnection tolocalhost:12345. - Sends the OTP message via
PrintWriter.
Key code:
// scenes/LogIn.java:274-281 new Thread(() -> { try (Socket s = new Socket("localhost", 12345); PrintWriter out = new PrintWriter(s.getOutputStream(), true)) { out.println(message); // OTP message sent to SMS server } catch (Exception ex) { ex.printStackTrace(); } }).start();
- When an OTP is generated, a new thread creates a
- Launch SMS Server first:
./run-sms.sh(startsSMSAppon port 12345) - User clicks "Forgot Password?" → enters registered phone number
- OTP is generated, then sent via socket to the SMS Server
- SMS Server receives and displays the OTP message in its UI
MainApp.java → HomePage.java → LogIn/SignUp → Dashboard (admin) | StudentProfile (student)
↓
Forgot Password? → SMSApp (port 12345, socket-based OTP)
- Password visibility toggle:
PasswordField+TextFieldswap viaHBoxcontainer with eye icon button Collage.java: hardcoded college name "Faculty of Science", dean, and departments- Window is fixed-size (
setResizable(false)inMainApp.java:27) - No build tool (Maven/Gradle), no tests, no linter configured