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);
     }
 }