A three-tier Java RMI application that replaces a paper-and-spreadsheet employee leave process with a single authoritative record served over TLS. Built for CT051-3-3-DCOMS (Distributed Computing Systems) at Asia Pacific University.
Crest Solutions tracked leave on desk copies and a shared spreadsheet. Two HR staff editing the same row would conflict, signed forms sat for weeks before reaching the sheet, and line managers routinely saw balances a quarter out of date — to the point that employees filed complaints over balances lower than the ones they had tracked themselves. This system makes the record centralized, concurrent-safe, and reachable from any desktop on the LAN.
CLI Client ──TLS/RMI──▶ HRMServerImpl ──JDBC──▶ Apache Derby
(employee / (business logic, (employees,
HR staff) thread pool) leave, credentials)
| Tier | Component | Responsibility |
|---|---|---|
| Presentation | hrm.client.HRMClient |
Command-line interface for employees and HR staff |
| Logic | hrm.server.HRMServerImpl |
Remote object implementing the service contract |
| Data | hrm.dao.* |
JDBC access to employees, leave applications, credentials |
| Shared | hrm.shared.* |
Serializable DTOs and the remote interface |
| Config | hrm.config.SecureRMIFactory |
TLS socket factories for both ends of the call |
Asynchronous leave evaluation. applyForLeave writes the application as PENDING, then hands evaluation to a fixed ExecutorService of five threads and returns immediately. The client stays responsive during a burst of submissions while the worker deducts the balance in the background. ServerMain registers a JVM shutdown hook so in-flight workers finish cleanly when the operator stops the server.
Two-layer concurrency control. Mutating remote methods (loginHR, registerEmployee, updatePersonalDetails, updateFamilyDetails, applyForLeave) are synchronized so concurrent clients cannot interleave writes to in-memory DAO state. The balance deduction itself is guarded at the SQL layer by a WHERE balance >= requested clause, so the database — not the application — is the final arbiter of whether leave can be granted.
TLS over RMI. SecureRMIFactory implements both RMIClientSocketFactory and RMIServerSocketFactory, swapping the default plain sockets for SSLSocketFactory / SSLServerSocketFactory when rmi.secure=true is set in db.properties. Because the factory travels to the client inside the stub, it must itself be Serializable — the client's socket behaviour then matches the server's automatically.
Serialization contract. Employee and LeaveApplication declare serialVersionUID = 1L so stub and skeleton agree on class shape across JVM versions. Employee carries balance, personal details, and family details as fields, so the HR client gets a complete snapshot in one round trip instead of five chatty calls.
HRMService exposes 16 methods across two actor groups.
HR staff — loginHR, registerEmployee, generateYearlyReport, getAllEmployees, getPendingApplications, approveLeave, rejectLeave, shutdownServer
Employees — updatePersonalDetails, updateFamilyDetails, checkLeaveBalance, applyForLeave, checkLeaveStatus, getLeaveHistory, getEmployeeDetails
The brief allowed any middleware. RMI won on four criteria specific to this deployment: the team works only in Java, the target is a single LAN, the load is roughly ten users, and the security requirement is satisfiable over TLS.
| RMI | CORBA | COM | MOM | WCF | |
|---|---|---|---|---|---|
| Language support | Java only | Many (via IDL) | Windows-centric | Agnostic | .NET |
| Transport | TCP, optional TLS | GIOP / IIOP | Windows RPC / DCOM | AMQP, MQTT | HTTP, TCP, MSMQ |
| Call model | Sync object invocation | Sync object invocation | Sync component call | Async messaging | Either, per binding |
| Runtime footprint | JVM only | ORB runtime | Windows + COM | Broker process | .NET runtime |
| Learning curve | Low for Java teams | Steep (IDL + ORB) | Steep | Moderate | Steep |
CORBA and WCF add toolchain and runtime cost without buying anything a single-language LAN deployment needs. COM would lock the system to Windows. MOM is the wrong call shape entirely — "what is my current leave balance?" is a request-response query, and broker indirection adds latency while complicating a pattern that RMI handles directly.
RMI runs on TCP, which supplies the ordering and delivery guarantees the domain requires: an applyForLeave that silently fails, or a generateYearlyReport returning half its rows out of order, breaks the trust model the system exists to fix. UDP was rejected for exactly that reason — its lower latency buys nothing when a leave transaction is worth far more than a few milliseconds. TLS sits above TCP because leave records and IC numbers are personal data that must not be readable by anything sitting between client and server on the LAN.
Requires JDK 8+ and Apache Derby on the classpath.
# compile
javac -d out $(find src -name "*.java")
# start the server (reads db.properties for rmi.secure, keystore path, port)
java -cp out:derby.jar hrm.server.ServerMain
# in a second terminal
java -cp out:derby.jar hrm.client.HRMClientSet rmi.secure=true in db.properties to enable TLS; the keystore path and password are injected as javax.net.ssl system properties by ServerMain and HRMClient.
- HR passwords are stored in plain text — the first thing to fix before any real use.
- The RMI registry is a single point of failure with no failover.
- Derby is embedded, so the database lives and dies with the server host.
Planned remediation: move Derby to a managed relational service (RDS or Azure Database for PostgreSQL) for automated backup and point-in-time recovery; containerize the RMI server and run two replicas behind a TCP load balancer; move the keystore and HR credentials into a secrets manager with scheduled rotation and read auditing.
Java · Java RMI · JDBC · Apache Derby · TLS/SSL · ExecutorService