Plain Java + Jetty WebSocket-based POS with JavaFX client
This POS system is implemented in plain Java using Jetty WebSockets for communication between the server and JavaFX client. Key features include:
The client connects to the server via WebSocket. Authentication occurs during connection:
Authorization: Basic <base64(username:password)>
Authorization: Bearer <jwtToken>
Server responds with JSON:
{
"type": "loginResponse",
"status": "success",
"message": "Authentication successful",
"token": "<jwtToken>"
}
Common WebSocket close codes handled:
Reconnection is handled automatically unless the user manually closes the client or authentication fails:
if (!reconnecting && !manualClose) {
attemptReconnect();
}
JWT tokens are reused for reconnections to reduce MySQL authentication hits.
Sales made while offline are stored locally:
List<Sale> offlineSales = OfflineSalesStorage.loadOfflineSales();
if (!offlineSales.isEmpty()) {
sendSales(offlineSales);
OfflineSalesStorage.clearOfflineSales();
}
This ensures sales are synced automatically once the connection is restored.
Some core methods available in PosWebSocketClient:
connectWithAuth() – connect with JWT if available, else Basic AuthsendSales(List<Sale>) – send sales to serversendGetProductsRequest(int page, int size) – request product listsendGetLowStockProducts(int threshold) – get low stock productscloseConnection(Runnable callback) – log out and close without reconnectUse nohup java -jar pos-server.jar & to start the server. Logs are written to nohup.out. Example:
tail -f nohup.out
Jetty debug logs include:
The system supports multiple types of reports:
Example request to server:
sendGetSalesBetween("2025-10-01", "2025-10-30");
sendGetLowStockProducts(10);
Products can be sorted and filtered by category for easier inventory management:
sendGetProductsByCategory("stationary", 1, 50);
sendGetProductsByCategory("ICT", 1, 50);
Category-wise stock value aggregation is supported:
sendRequestStockValueByCategory();
The system enforces role-based access:
Server ensures that actions are validated against user roles:
if(user.getRole() == Role.CASHIER) {
// allow sale
} else {
// block sensitive operations
}