ENROLIUM is a system we built to make section selection smoother and more user-friendly. We worked on both Frontend and Backend, focusing on giving everyone a fair chance during the selection process and making the system easy to use. This trimester’s section selection process went way better than in previous terms. Last time, everyone had a tough time with section selection, and most couldn't get their preferred slots, which really frustrated them. From these difficulties, the idea for ENROLLIUM was born—a platform designed to simplify and enhance the section selection process for students.
The system consists of both a server and a client that communicate via a custom RPC. To establish a connection, users must provide their email and password. The entire system maintains a single source of truth, which is the database.
Layer | Technology | Purpose |
---|---|---|
Language | Java 23 | Core application runtime |
Build | Gradle 8.10.2 | Build automation and dependency management |
Frontend | JavaFX 23, Atlantafx 2.0.1 | Modern user interface components |
Ikonli 12.3.1 | Icon library integration | |
CSSFX 11.5.1 | Dynamic CSS styling management | |
UI Utilities | DataFaker 2.4.2 | Mock data generation for UI development |
Reactive Programming | RxJava 3.1.9 | Asynchronous stream processing |
Communication | Jackson JSON 2.18.2 | RPC payload serialization/deserialization |
Database | Hibernate ORM 6.6.3.Final | Object-relational mapping |
PostgreSQL JDBC 42.7.4 | Database connectivity | |
HikariCP 6.2.1 | Connection pooling optimization | |
Validation | Jakarta Validation 3.1.0 | Data validation framework |
Hibernate Validator 8.0.1.Final | Constraint enforcement implementation | |
Security | BCrypt 0.10.2 | Password hashing and verification |
Logging | Logback 1.4.11 | Application logging framework |
Testing | JUnit 5.10.3 | Unit testing framework |
AssertJ 3.27.2 | Fluent test assertions | |
System Monitoring | OSHI 6.6.6 | Hardware/OS metrics collection |
Utility | Commons Text 1.12.0 | String manipulation utilities |
Lombok 1.18.36 | Boilerplate code reduction | |
JetBrains Annotations 26.0.1 | Static analysis hints |
Implementation Status: Complete
- Single source of truth.
- Enforces ACID-compliant transactions for all critical operations.
- Supports complex relationships (e.g., prerequisites, trimester phases, section-teacher assignments).
- Generates demo data.
Table | Description | Relationships & Constraints |
---|---|---|
users |
Base user entity (login, name, password). | - Inherited by students /faculty via user_id (joined-table). - email (UNIQUE, NOT NULL). |
students |
Student-specific data (e.g., university ID). | - user_id (PK, FK to users.id ) - university_id (UNIQUE, NOT NULL). |
faculty |
Faculty members (e.g., shortcode). | - user_id (PK, FK to users.id ) - shortcode (UNIQUE, NOT NULL). |
subjects |
Academic courses (e.g., credits, type). | - code_name (UNIQUE, NOT NULL) - credits (1–5) - type (THEORY/LAB). |
trimesters |
Academic periods (e.g., status, dates). | - code (UNIQUE, format `YY[1 |
sections |
Class sections (e.g., capacity, time slots). | - trimester_id (FK to trimesters.id ) - subject_id (FK to subjects.id ) - max_capacity (≥1) - space_time_slots (NOT EMPTY). |
courses |
Student enrollments (e.g., status, grade). | - Unique constraint: (student_id, subject_id, trimester_id, section_id) - grade (0.0–4.0) - status validates section /grade compatibility. |
prerequisites |
Subject dependencies (e.g., "CSE3521 requires CSE1116"). | - subject_id (FK to subjects.id ) - prerequisite_id (FK to subjects.id ) - No cycles (code-enforced). |
space_time |
Physical/time slots for sections. | - Unique constraint: (room_number, day_of_week, timeslot) - time_slot validated by room_type . |
notifications |
System announcements (e.g., scope, category). | - scope determines trimester /section /target_user validity (e.g., SECTION requires non-null section_id ). |
Table | Purpose | Structure |
---|---|---|
faculty_subjects |
Faculty’s teachable subjects. | faculty_id (FK to faculty.user_id ), subject_id (FK to subjects.id ). |
section_space_times |
Assigns time slots to sections. | section_id (FK to sections.id ), space_time_id (FK to space_time.id ). |
section_faculty |
Assigns teachers to sections. | section_id (FK to sections.id ), faculty_id (FK to faculty.user_id ). |
The DB
class provides reactive CRUD operations via Hibernate and RxJava. Below are common use cases:
// Create a new student
Student student = new Student();
student.setName("John Doe");
student.setEmail("[email protected]");
student.setPassword("securePass123");
student.setUniversityId(112233445);
// Persist to DB
DB.save(student)
.subscribe(
savedStudent -> System.out.println("Saved: " + savedStudent.getId()),
error -> System.err.println("Error: " + error.getMessage())
);
// Fetch first 10 courses, sorted by creation date
DB.read(Student.class, limit, offset)
.timeout(20, TimeUnit.SECONDS)
.collect(ArrayList::new, (list, item) -> list.add(item))
.onErrorResumeNext(error -> new RuntimeException("Failed to fetch student list: " + error.getMessage()));
// Update a student's email
DB.findById(Student.class, UUID.fromString(id))
.toSingle()
.flatMap(student -> {
student.setName(name);
return DB.update(student);
})
.onErrorResumeNext(error -> new RuntimeException("Failed to update student: " + error.getMessage()));
// Delete a notification by ID
DB.delete(Student.class, UUID.fromString(id))
.subscribe(
() -> System.out.println("Deleted successfully"),
error -> System.err.println("Deletion failed: " + error.getMessage())
);
// Count registered courses for a student
DB.count(Course.class)
.map(total -> "Total courses: " + total)
.subscribe(System.out::println);
// Check if a section is full
DB.findById(Section.class, sectionId)
.map(section -> section.getCurrentCapacity() >= section.getMaxCapacity())
.subscribe(isFull -> System.out.println("Section full? " + isFull));
// Wipe and re-seed with demo data
DB.resetAndSeed()
.subscribe(
() -> System.out.println("Database reset complete"),
error -> System.err.println("Reset failed: " + error.getMessage())
);
For custom transactions (e.g., batch updates):
DB.exec(session -> {
// Manual Hibernate operations
session.createMutationQuery("UPDATE Course SET grade = 4.0 WHERE status = 'COMPLETED'").executeUpdate();
return null;
}, "Batch Grade Update").subscribe();
- Cache frequently accessed entities like
subjects
andtrimesters
. - Implement database-level triggers for cross-table validations (e.g., section capacity checks).
Implementation Status: Complete
- Enables real-time client-server communication with session-aware routing
- Handles 150+ concurrent requests with automatic retries and timeouts
- Provides atomic message delivery with JSON serialization
- Supports server-initiated notifications (e.g., schedule changes)
-
Protocol:
- Message framing: 4-byte length header + JSON payload
- Type hierarchy:
Message
→Request
/Response
- Session binding via
sessionToken
field
-
Sequence:
- Client establishes TCP connection
- Authentication handshake via
auth
method - Subsequent requests use session token
- Server pushes notifications via same channel
Code Snippet:
// Client-side
ObjectNode authParams = JsonUtils.createObject().put("email", email).put("password", password);
Request authRequest = Request.create(messageIdCounter.getAndIncrement(), "auth", authParams, null);
Response authResponse = ClientRPC.getInstance().sendRequest(authRequest).timeout(5, TimeUnit.SECONDS).blockingGet();
// Server handler registration
server.registerMethod("auth", (params, request) -> {
try {
// Validate request parameters
if (params == null) return Single.error(new IllegalArgumentException("Missing auth parameters"));
String email = JsonUtils.getString(params, "email");
String password = JsonUtils.getString(params, "password");
if (!(email != null && password != null && !email.trim().isEmpty() && !password.trim().isEmpty()))
return Single.error(new IllegalArgumentException("Invalid credentials"));
int size = Math.toIntExact(DB.count(User.class).blockingGet());
return DB.read(User.class, size, 0)
.filter(user -> email.equals(user.getEmail()))
.firstElement()
.toSingle()
.flatMap(user -> {
if (!user.verifyPassword(password)) {
return Single.error(new IllegalArgumentException("Invalid password"));
}
// Create session
String UUID = user.getId().toString();
SessionInfo session = SessionManager.getInstance().createSession(UUID, request.getConnection().getSocket(), server);
// Return success response
JsonNode response = JsonUtils.createObject()
.put("sessionToken", session.getSessionToken())
.put("uuid", UUID)
.put("userType", user.getType().toString());
return Single.just(response);
})
.onErrorResumeNext(error -> {
if (error instanceof NoSuchElementException) return Single.error(new IllegalArgumentException("User not found"));
return Single.error(error);
});
} catch (Exception e) {
return Single.error(e);
}
});
Message Structure:
- Request
{
"id": 1,
"timestamp": 1738415230282,
"version": "r145.63cc2b3",
"type": "req",
"method": "auth",
"params": {
"email": "[email protected]",
"password": "WrongPass"
},
"sessionToken": null,
"connection": null
}
- Success Response
{
"id": 1,
"timestamp": 1738415232717,
"version": "r145.63cc2b3",
"type": "res",
"method": "success",
"params": {
"sessionToken": "9361147112443298276352683881617408806308841024330508058726967887",
"uuid": "af0f9edf-73c3-47cc-85c7-851e1ea99f36",
"userType": "STUDENT"
},
"error": false,
"errorMessage": null
}
- Error Response
{
"id": 1,
"timestamp": 1738415230525,
"version": "r145.63cc2b3",
"type": "res",
"method": "error",
"params": {
"message": "Invalid password"
},
"error": true,
"errorMessage": "Invalid password"
}
- Heartbeat
{
"id": 8,
"timestamp": 1738415442684,
"version": "r145.63cc2b3",
"type": "req",
"method": "health",
"params": null,
"sessionToken": "9361147112443298276352683881617408806308841024330508058726967887",
"connection": null
}
{
"id": 8,
"timestamp": 1738415442686,
"version": "r145.63cc2b3",
"type": "res",
"method": "success",
"params": {
"serverVersion": "r145.63cc2b3",
"status": "ok",
"serverTime": 1738415442686
},
"error": false,
"errorMessage": null
}
Connection Lifecycle:
- Client connects via
ClientRPC.start()
- Auth exchange using credentials
- Server creates
SessionInfo
with 24h TTL - Heartbeats reset TTL on each message
- Cleanup thread removes expired sessions
- Add encryption in
RPCConnection
- Session token rotation
- Protobuf for serialization
- Message compression for large payloads
- Idempotency keys for retries
Implementation Status: Complete
- Maintains authenticated user sessions with time-to-live (TTL) tracking
- Binds network connections to user identities for stateful communication
- Automatically cleans up expired/inactive sessions to free resources
- Session Creation:
- Generates unique 64-character token via
SecureRandom
on successful authentication - Stores session metadata (user ID, connection, timestamps) in
ConcurrentHashMap
- Generates unique 64-character token via
- Validation:
- Checks
isActive()
flag andexpirationTime
on every authenticated request - Updates
lastHeartbeat
timestamp on valid operations
- Checks
- Cleanup:
- Scheduled thread scans sessions every 1 minute
- Removes sessions exceeding 24-hour TTL or 30-second heartbeat timeout
{
"9361147112443298276352683881617408806308841024330508058726967887" : {
"sessionToken" : "9361147112443298276352683881617408806308841024330508058726967887",
"userId" : "af0f9edf-73c3-47cc-85c7-851e1ea99f36",
"tags" : [ ],
"createdAt" : 1738415232717,
"lastHeartbeat" : 1738415262686,
"expirationTime" : 1738501662686,
"active" : true,
"expired" : false,
"ip" : "127.0.0.1"
}
}
- Enforces request quotas to prevent system overload
- Applies different limits for unauthenticated (IP-based) and authenticated (session-based) traffic
- Protects against DDoS and brute-force attacks
- Counting Mechanism:
- Uses
ConcurrentHashMap
to track request counts per IP/session - Merges counts atomically using
Integer::sum
- Uses
- Limit Enforcement:
- Denies requests exceeding 32/minute for IPs (pre-auth)
- Allows 512/minute for authenticated sessions
- Periodic Reset:
- Scheduled task clears all counters every 60 seconds
{
"9361147112443298276352683881617408806308841024330508058726967887" : 1,
"127.0.0.1" : 4
}
Implementation Status: Complete
- Captures critical system metadata during startup
- Logs Java properties, environment variables, and hardware specs
- Provides runtime diagnostics for troubleshooting
-
Entry Point:
Issue.print(logger)
called during application bootstrap
-
Data Collection:
- System Properties via
System.getProperties()
- Environment Variables via
System.getenv()
- Hardware Metrics using
OperatingSystemMXBean
- System Properties via
-
Formatting:
- Section headers with indented key-value pairs
- Memory values auto-converted to GB
Example Output:
JAVA SYSTEM PROPERTIES:
file.encoding: UTF-8
java.class.version: 67.0
java.home: /usr/lib/jvm/java-23-openjdk
...
ENVIRONMENT VARIABLES:
BROWSER: firefox
EDITOR: micro
LANG: en_US.UTF-8
LANGUAGE: en_US:en:C
LC_ADDRESS: C.utf8
LC_COLLATE: C.utf8
LC_CTYPE: en_US.UTF-8
LC_IDENTIFICATION:
LC_MEASUREMENT: C.utf8
LC_MESSAGES: en_US.UTF-8
LC_MONETARY: en_US.UTF-8
LC_NAME: en_US.UTF-8
LC_NUMERIC: en_US.UTF-8
LC_PAPER: en_US.UTF-8
LC_TELEPHONE: C.utf8
LC_TIME: C.utf8
...
HARDWARE INFORMATION:
OS Name: Linux
OS Version: 6.12.10-zen1-1-zen
OS Architecture: amd64
Available Processors (Cores): 8
Total Physical Memory: 23 GB
Free Physical Memory: 8 GB
Total Swap Space: 11 GB
Free Swap Space: 6 GB
- Essential for debugging environment-specific issues
- Provides visibility into resource constraints
- Add disk space monitoring
- Periodic runtime snapshots
- Integration with monitoring systems like Prometheus
Implementation Status: Complete
- Generates version strings from Git history
- Formats as
r<commit_count>.<short_hash>
(e.g.,r145.63cc2b3
) - Automatically generates new one at build
String v = Version.getVersion();
- Build process executes
generateVersion()
Gradle task - Writes version to
src/main/resources/version.properties
- Application loads via static initializer
- Eliminates manual version tracking
- Links deployments to exact code states
- Nightly build automation
- Version metadata endpoint in RPC system
Implementation Status: Partial (90%)
Demo:
- Can support more languages by editing an enum
- Centralized translation management
- Hot-reload language without restart
-
Resource Bundles:
messages_en.properties
: English translationsmessages_bn.properties
: Bengali translations
-
Validation:
- Startup check for missing keys in all bundles
-
Reactive Updates:
SettingsManager
notifiesI18nManager
of language changes
Usage:
String greeting = I18nManager.instance.get(TranslationKey.HELLO);
- Add right-to-left (RTL) layout support
- Dynamic bundle reloading from filesystem
- Pluralization rules implementation
Implementation Status: Complete
- Thread-safe shared memory for cross-component data
- Stores session tokens, UI states, and temporary workflows
// Store current user
Volatile.getInstance().put("currentUser", user);
// Retrieve chat history
List<Message> chat = (List<Message>) Volatile.getInstance().get("activeChat");
Key Features:
- ConcurrentHashMap backend for scalability
- Audit logging on write/delete operations
- Singleton access via double-checked locking
- TTL-based auto-expiry for entries
- Size monitoring and eviction policies
- Cluster-aware replication for distributed mode
Implementation Status: Complete
- Maintains all user preferences across sessions
- OS-appropriate config file storage
- Type-safe validation with reactive updates
Key Features:
- Cross-platform config paths:
- Windows:
%APPDATA%\enrollium\settings.json
- Linux:
~/.config/enrollium/settings.json
- macOS:
~/Library/Application Support/enrollium/settings.json
- Windows:
- Versioned schema migrations
- Debounced autosave (1-second delay)
- Essential for personalized user experience
- Reduces setup friction across devices
- Cloud sync via RPC system
- Settings import/export UI
- Historical version rollback
Implementation Status: Complete
- Core Functionality: Enables real-time chat between users with persistent message history.
- User Impact: Provides a seamless communication experience with intuitive UI, message history, and system notifications.
Implementation Status: Complete
- Displays a list of offered courses with filtering options (Trimester, Course Type, Search).
- Allows students to select, register, withdraw, or retake courses dynamically.
- Provides real-time status updates on course selection.
- Business Need: Enhances the course enrollment process for students.
- Technical Rationale: Reduces server load by implementing efficient RPC calls and caching.
- Performance: Optimize filtering logic using indexed lookups.
- Functionality: Implement bulk course selection.
- UX: Improve accessibility by adding keyboard navigation support.
Implementation Status: Partial
- Enables users to manage course sections, including subject, trimester, and capacity.
- Provides real-time registration status with progress tracking and statistics.
- Performance: Optimize table refresh logic for large datasets.
- Functionality: Allow bulk section creation and edits.
- UX: Improve notification system and introduce filtering options for section searches.
Implementation Status: Partial
- Enables students to swap course sections or offer trades with others.
- Provides an interactive UI for selecting sections, proposing trades, and accepting offers.
- Uses a real-time update mechanism to ensure trade availability stays current.
- Business Need: Allows students to self-manage section changes, reducing admin workload.
- Technical Rationale: Replaces a manual trade request system, improving efficiency.
- Performance: Cache frequently accessed trade data to reduce API calls.
- Functionality: Add bulk trade requests to facilitate multiple swaps at once.
- UX: Provide visual indicators for pending, accepted, or rejected trades.
Implementation Status: Complete
- Provides real-time monitoring of server performance metrics.
- Displays CPU, RAM, Disk, and Network usage via line charts.
- Fetches and updates server statistics dynamically every second.
Patterns/Concepts:
- Observer pattern for UI updates.
- Timeline-based periodic data fetching.
- Asynchronous RPC calls for server communication.
- Business Need: Ensures real-time monitoring for system administrators to track performance and prevent downtimes.
- Technical Rationale: Replaces manual log-based tracking with a dynamic, interactive dashboard.
- Performance: Optimize data handling to prevent UI lag when updating charts.
- Functionality: Add historical data visualization beyond the last 30 seconds.
- UX: Implement tooltips and additional analytics for deeper insights into server performance trends.
Implementation Status: Partial
- Enables sending notifications with customizable title, content, category, and scope.
- Provides options to create, update, and delete notifications.
- Supports quick demo content filling for testing purposes.
Patterns/Concepts:
- Observer pattern for UI updates.
- Form-based input validation.
- Modular UI components for reusability.
- Business Need: Facilitates real-time communication with users.
- Technical Rationale: Provides a structured and user-friendly notification system, replacing ad-hoc message handling.
- Performance: Optimize UI responsiveness for large-scale notification batches.
- Functionality: Add scheduling support for delayed notifications.
- UX: Implement rich-text support for better message formatting.
Implementation Status: Partial
- Provides an interface for students to submit withdrawal requests.
- Allows administrators to review, approve, or reject requests.
- Displays real-time updates on the request status.
- Observer Pattern: Used for updating the UI dynamically when a request is processed.
- MVC Architecture: Separates request handling logic from UI components.
- State Management: Requests can exist in different states: Pending, Approved, Rejected.
- Business Need: Ensures a structured process for students to withdraw from courses.
- Technical Rationale: Replaces manual processing with a digital workflow to improve efficiency.
- Performance: Optimize UI rendering with lazy loading for large datasets.
- Functionality: Implement batch processing for multiple requests at once.
- UX: Add filtering and sorting options for withdrawal requests.
Implementation Status: Partial
Key Features
- CRUD for all infos in db.
- Keeps everything organized and easy to access and modify.
Future Improvements
- Add more filters for better search options.
- Sync changes across the platform instantly.
- Provide insights into enrollment trends.
Implementation Status: Complete
- This shows a students all the course list.
Future Improvements
- Better UI.
- Better statistics.
- Better actions.
Implementation Status: Complete
- You can easily find the page you want.
- You can press the
/
button to open the search dialogue.
Implementation Status: Complete
Key Features
- Secure login with passwords.
- Reset forgotten passwords via email.
Future Improvements
- Use magic links or one-time codes instead of passwords.
- Notify users of suspicious activity on their account.
- Let users log in once to access multiple platforms.
Implementation Status: Complete
Key Features
- Switch between light and dark mode.
- Choose from pre-designed themes or create your own.
Future Improvements
- Adjust themes based on lighting or user preferences.
- Let users customize fonts, spacing, and colors.
- Add fun themes for holidays or special events.