diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java index 6c30307..d3ac3c0 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import java.util.WeakHashMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jspecify.annotations.Nullable; @@ -19,8 +20,14 @@ import org.jspecify.annotations.Nullable; */ public class ChatController { + /** Ensures one active message poller per physical ClientService connection. */ + private static final Map ACTIVE_CONTROLLERS = + new WeakHashMap<>(); + private final String username; + private final ClientService clientService; + private final ChatClient chatClient; public ChatBoxController getChatBoxController() { @@ -58,11 +65,15 @@ public class ChatController { */ public ChatController(String username, ClientService clientService) { this.username = username; + this.clientService = clientService; chatClient = new ChatClient(clientService); chatModelMap = new LinkedHashMap<>(); localUserList = new ArrayList<>(); this.chatBoxController = new ChatBoxController(username, this); this.logger = LogManager.getLogger(ChatController.class); + + registerAsActiveController(clientService); + this.timer = new Timer(true); timer.schedule( new TimerTask() { @@ -80,6 +91,17 @@ public class ChatController { REFRESH_TIME); } + private void registerAsActiveController(ClientService clientService) { + synchronized (ACTIVE_CONTROLLERS) { + ChatController oldController = ACTIVE_CONTROLLERS.get(clientService); + if (oldController != null && oldController != this) { + oldController.shutdown(); + logger.info("Replaced previous ChatController poller for shared ClientService"); + } + ACTIVE_CONTROLLERS.put(clientService, this); + } + } + /** * Method to be activated, if a lobby has been chosen. It will update the UI and add a new * ChatModel to hold the Data for the Lobby Chat @@ -158,8 +180,8 @@ public class ChatController { } /** - * Method to get the lobbyId of the currently active lobby chat, to check if incoming lobby messages - * belong to the same lobby. + * Method to get the lobbyId of the currently active lobby chat, to check if incoming lobby + * messages belong to the same lobby. * * @return the lobbyId of the currently active lobby chat. */ @@ -210,6 +232,11 @@ public class ChatController { /** Stops polling background tasks for this chat controller instance. */ public void shutdown() { timer.cancel(); + synchronized (ACTIVE_CONTROLLERS) { + if (ACTIVE_CONTROLLERS.get(clientService) == this) { + ACTIVE_CONTROLLERS.remove(clientService); + } + } } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/send_message/SendMessageHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/send_message/SendMessageHandler.java index 94ee193..a2bd3ad 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/send_message/SendMessageHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/send_message/SendMessageHandler.java @@ -1,7 +1,8 @@ package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.send_message; -import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message; import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatType; +import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.Lobby; import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyId; import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyManager; import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry; @@ -38,7 +39,7 @@ public class SendMessageHandler extends CommandHandler { @Override public void execute(SendMessageRequest request) { Message message = request.getMessage(); - broadcast(message); + broadcast(request, message); OkResponse response = new OkResponse(request.getContext()); responseDispatcher.dispatch(response); } @@ -49,20 +50,35 @@ public class SendMessageHandler extends CommandHandler { * * @param message The {@link Message} object to be broadcast. */ - public void broadcast(Message message) { - if (message != null - && message.getMessageType() == ChatType.LOBBY - && message.lobbyId >= 0 - && lobbyManager != null) { - lobbyManager.broadcast( - LobbyId.of(message.lobbyId), - username -> - userRegistry - .getByUsername(username) - .ifPresent(user -> user.enqueueMessage(message))); - return; + public void broadcast(SendMessageRequest request, Message message) { + if (message != null && message.getMessageType() == ChatType.LOBBY && lobbyManager != null) { + LobbyId targetLobbyId = resolveLobbyIdForMessage(request, message); + if (targetLobbyId != null) { + lobbyManager.broadcast( + targetLobbyId, + username -> + userRegistry + .getByUsername(username) + .ifPresent(user -> user.enqueueMessage(message))); + return; + } } userRegistry.getAllUsers().forEach(user -> user.enqueueMessage(message)); } + + private LobbyId resolveLobbyIdForMessage(SendMessageRequest request, Message message) { + if (message.lobbyId >= 0) { + LobbyId idFromMessage = LobbyId.of(message.lobbyId); + if (lobbyManager.getLobby(idFromMessage) != null) { + return idFromMessage; + } + } + + return userRegistry + .getBySessionId(request.getContext().sessionId()) + .map(user -> lobbyManager.getLobbyByUsername(user.getName())) + .map(Lobby::getId) + .orElse(null); + } }