Fix: lobyb chat race condition
This commit is contained in:
@@ -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<ClientService, ChatController> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+24
-8
@@ -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<SendMessageRequest> {
|
||||
@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<SendMessageRequest> {
|
||||
*
|
||||
* @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) {
|
||||
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(
|
||||
LobbyId.of(message.lobbyId),
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user