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.Map;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
@@ -19,8 +20,14 @@ import org.jspecify.annotations.Nullable;
|
|||||||
*/
|
*/
|
||||||
public class ChatController {
|
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 String username;
|
||||||
|
|
||||||
|
private final ClientService clientService;
|
||||||
|
|
||||||
private final ChatClient chatClient;
|
private final ChatClient chatClient;
|
||||||
|
|
||||||
public ChatBoxController getChatBoxController() {
|
public ChatBoxController getChatBoxController() {
|
||||||
@@ -58,11 +65,15 @@ public class ChatController {
|
|||||||
*/
|
*/
|
||||||
public ChatController(String username, ClientService clientService) {
|
public ChatController(String username, ClientService clientService) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
|
this.clientService = clientService;
|
||||||
chatClient = new ChatClient(clientService);
|
chatClient = new ChatClient(clientService);
|
||||||
chatModelMap = new LinkedHashMap<>();
|
chatModelMap = new LinkedHashMap<>();
|
||||||
localUserList = new ArrayList<>();
|
localUserList = new ArrayList<>();
|
||||||
this.chatBoxController = new ChatBoxController(username, this);
|
this.chatBoxController = new ChatBoxController(username, this);
|
||||||
this.logger = LogManager.getLogger(ChatController.class);
|
this.logger = LogManager.getLogger(ChatController.class);
|
||||||
|
|
||||||
|
registerAsActiveController(clientService);
|
||||||
|
|
||||||
this.timer = new Timer(true);
|
this.timer = new Timer(true);
|
||||||
timer.schedule(
|
timer.schedule(
|
||||||
new TimerTask() {
|
new TimerTask() {
|
||||||
@@ -80,6 +91,17 @@ public class ChatController {
|
|||||||
REFRESH_TIME);
|
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
|
* 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
|
* 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
|
* Method to get the lobbyId of the currently active lobby chat, to check if incoming lobby
|
||||||
* belong to the same lobby.
|
* messages belong to the same lobby.
|
||||||
*
|
*
|
||||||
* @return the lobbyId of the currently active lobby chat.
|
* @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. */
|
/** Stops polling background tasks for this chat controller instance. */
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
timer.cancel();
|
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;
|
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.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.LobbyId;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyManager;
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyManager;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||||
@@ -38,7 +39,7 @@ public class SendMessageHandler extends CommandHandler<SendMessageRequest> {
|
|||||||
@Override
|
@Override
|
||||||
public void execute(SendMessageRequest request) {
|
public void execute(SendMessageRequest request) {
|
||||||
Message message = request.getMessage();
|
Message message = request.getMessage();
|
||||||
broadcast(message);
|
broadcast(request, message);
|
||||||
OkResponse response = new OkResponse(request.getContext());
|
OkResponse response = new OkResponse(request.getContext());
|
||||||
responseDispatcher.dispatch(response);
|
responseDispatcher.dispatch(response);
|
||||||
}
|
}
|
||||||
@@ -49,20 +50,35 @@ public class SendMessageHandler extends CommandHandler<SendMessageRequest> {
|
|||||||
*
|
*
|
||||||
* @param message The {@link Message} object to be broadcast.
|
* @param message The {@link Message} object to be broadcast.
|
||||||
*/
|
*/
|
||||||
public void broadcast(Message message) {
|
public void broadcast(SendMessageRequest request, Message message) {
|
||||||
if (message != null
|
if (message != null && message.getMessageType() == ChatType.LOBBY && lobbyManager != null) {
|
||||||
&& message.getMessageType() == ChatType.LOBBY
|
LobbyId targetLobbyId = resolveLobbyIdForMessage(request, message);
|
||||||
&& message.lobbyId >= 0
|
if (targetLobbyId != null) {
|
||||||
&& lobbyManager != null) {
|
|
||||||
lobbyManager.broadcast(
|
lobbyManager.broadcast(
|
||||||
LobbyId.of(message.lobbyId),
|
targetLobbyId,
|
||||||
username ->
|
username ->
|
||||||
userRegistry
|
userRegistry
|
||||||
.getByUsername(username)
|
.getByUsername(username)
|
||||||
.ifPresent(user -> user.enqueueMessage(message)));
|
.ifPresent(user -> user.enqueueMessage(message)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
userRegistry.getAllUsers().forEach(user -> user.enqueueMessage(message));
|
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