From c04d4fca768b4d61aca681acb88e9e554f741a35 Mon Sep 17 00:00:00 2001 From: Jona Walpert Date: Fri, 24 Apr 2026 14:14:59 +0200 Subject: [PATCH] Refactor: Add LobbyCleanupJob for periodic expired lobby cleanup - isolate failures per lobby during cleanup - notify connected sessions about closed lobbies Refs #124 Co-authored-by: Copilot --- .../server/domain/lobby/LobbyCleanupJob.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyCleanupJob.java diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyCleanupJob.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyCleanupJob.java new file mode 100644 index 0000000..47c0413 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyCleanupJob.java @@ -0,0 +1,100 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher; +import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session; +import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager; +import java.time.Duration; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** Periodically removes expired empty lobbies and notifies connected sessions. */ +public class LobbyCleanupJob implements Runnable { + + private static final Logger LOGGER = LogManager.getLogger(LobbyCleanupJob.class); + + private final LobbyManager lobbyManager; + private final SessionManager sessionManager; + private final ResponseDispatcher responseDispatcher; + private final Duration expiryThreshold; + + /** + * Creates a new cleanup job for expired empty lobbies. + * + * @param lobbyManager manager used to find and remove expired lobbies + * @param sessionManager manager used to resolve connected sessions + * @param responseDispatcher dispatcher used to notify clients about closed lobbies + * @param expiryThreshold age threshold that marks an empty lobby as expired + */ + public LobbyCleanupJob( + LobbyManager lobbyManager, + SessionManager sessionManager, + ResponseDispatcher responseDispatcher, + Duration expiryThreshold) { + this.lobbyManager = lobbyManager; + this.sessionManager = sessionManager; + this.responseDispatcher = responseDispatcher; + this.expiryThreshold = expiryThreshold; + } + + /** + * Runs one cleanup cycle. + * + *

The method first fetches all expired empty lobbies. Each lobby is then processed + * independently so that a failure for one lobby does not stop the remaining cleanups. + */ + @Override + public void run() { + LOGGER.debug("Job started."); + try { + List expired; + try { + expired = lobbyManager.findEmptyLobbiesOlderThan(expiryThreshold); + } catch (Exception e) { + LOGGER.error("Lobby expiry job failed: could not fetch expired lobbies", e); + return; + } + + for (LobbyId lobbyId : expired) { + try { + lobbyManager.removeLobby(lobbyId); + broadcastLobbyClosed(lobbyId); + } catch (RuntimeException e) { + LOGGER.warn("Failed to process expired lobby {}", lobbyId.value(), e); + } + } + } finally { + LOGGER.debug("Job finished."); + } + } + + /** + * Broadcasts a lobby-closed event to all currently connected sessions. + * + * @param lobbyId id of the lobby that was closed + */ + private void broadcastLobbyClosed(LobbyId lobbyId) { + for (Session session : sessionManager.getAllSessions()) { + try { + RequestContext ctx = new RequestContext(session.getId(), 0); + SuccessResponse event = + new SuccessResponse( + ctx, + ResponseBody.builder() + .param("EVENT", "LOBBY_CLOSED") + .param("LOBBY_ID", lobbyId.value()) + .build()) {}; + responseDispatcher.dispatch(event); + } catch (RuntimeException e) { + LOGGER.warn( + "Failed to notify session {} about closed lobby {}", + session.getId().value(), + lobbyId.value(), + e); + } + } + } +}