Coverage Summary for Class: RMIHeartbeat (it.polimi.ingsw.gc14.Network.RMI.Server)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
| RMIHeartbeat |
0%
(0/1)
|
0%
(0/6)
|
0%
(0/4)
|
0%
(0/24)
|
package it.polimi.ingsw.gc14.Network.RMI.Server;
import it.polimi.ingsw.gc14.LimitedMap;
import it.polimi.ingsw.gc14.Network.NetworkEvent;
import it.polimi.ingsw.gc14.Network.NetworkConfig;
import it.polimi.ingsw.gc14.Network.NetworkEvents.DisconnectedPlayer;
import it.polimi.ingsw.gc14.Network.RMI.Common.IClientCallback;
import java.util.Map;
import java.util.concurrent.*;
/**
* Server-side heartbeat watchdog for a single RMI client.
*
* <p>Mirrors {@code HeartbeatHandler} used in the TCP stack, but adapted for RMI:
* instead of reading raw bytes from a dedicated socket, it relies on {@link #receivePing()}
* being called by {@link RMIServer#ping(String)} every time the client sends a ping.
*
* <p>If no ping is received within {@value NetworkConfig#SILENCE_THRESHOLD_MS} ms, the player is
* considered disconnected and {@link #disconnect()} is invoked, which:
* <ul>
* <li>stops the watchdog;</li>
* <li>marks the player as offline in {@code playerList};</li>
* <li>removes the callback from {@code clients};</li>
* <li>adds a {@link DisconnectedPlayer} event to the action queue.</li>
* </ul>
*/
public class RMIHeartbeat {
/** Username of the client monitored by this watchdog. */
private String username;
/** Shared map used to mark the player as offline on timeout. */
private final LimitedMap<String, Boolean> playerList;
/** Registry of active RMI callbacks; the entry for this player is removed on disconnect. */
private final Map<String, IClientCallback> clients;
/** Shared event queue; a {@link DisconnectedPlayer} event is posted here on timeout. */
private final BlockingQueue<NetworkEvent> actionQueue;
/** Last time a ping was received from this client. */
private volatile long lastPingTime = System.currentTimeMillis();
/** {@code true} while the watchdog is running; set to {@code false} on first disconnect. */
private volatile boolean running = true;
/** Single-thread executor that periodically checks whether the silence threshold has been exceeded. */
private final ScheduledExecutorService watchdog =
Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r, "rmi-watchdog-" + username);
t.setDaemon(true);
return t;
});
/**
* Constructs an RMI heartbeat handler for the specified client.
*
* @param username the username of the client monitored by the heartbeat.
* @param playerList the map storing the connection status of the players.
* @param clients the collection of currently registered RMI clients.
* @param actionQueue the queue containing network events to be processed.
*/
public RMIHeartbeat(
String username,
LimitedMap<String, Boolean> playerList,
Map<String, IClientCallback> clients,
BlockingQueue<NetworkEvent> actionQueue) {
this.username = username;
this.playerList = playerList;
this.clients = clients;
this.actionQueue = actionQueue;
}
/** Called by {@link RMIServer} whenever it starts tracking this player. */
public void start() {
watchdog.scheduleAtFixedRate(() -> {
if (System.currentTimeMillis() - lastPingTime > NetworkConfig.SILENCE_THRESHOLD_MS) {
System.out.println("RMI heartbeat timeout: " + username);
disconnect();
}
}, 1, 1, TimeUnit.SECONDS);
}
/**
* Called by {@link RMIServer#ping(String)} each time the client pings.
* Resets the silence timer — mirrors writing {@code lastReceivedTime} in
* {@code HeartbeatHandler}.
*/
public void receivePing() {
lastPingTime = System.currentTimeMillis();
}
/**
* Disconnects the monitored RMI client.
*
* <p>The heartbeat handler is stopped, the player is marked as offline,
* the associated RMI callback is removed, and a disconnection event is
* added to the action queue for server-side processing.
*/
public void disconnect() {
if (!running) return;
running = false;
watchdog.shutdownNow();
playerList.put(username, false);
clients.remove(username);
actionQueue.add(new DisconnectedPlayer(username));
System.err.println("(RMI) Disconnected player: " + username);
}
}