Coverage Summary for Class: KeepAliveSocketWrapper (it.polimi.ingsw.Network)

Class Class, % Method, % Branch, % Line, %
KeepAliveSocketWrapper 0% (0/1) 0% (0/3) 0% (0/10) 0% (0/32)


1 package it.polimi.ingsw.Network; 2  3 import it.polimi.ingsw.Logger; 4 import it.polimi.ingsw.Server.Messages.HeartBeatMessage; 5 import it.polimi.ingsw.Server.Messages.Message; 6  7 import java.io.IOException; 8 import java.net.Socket; 9 import java.net.SocketException; 10 import java.util.Timer; 11 import java.util.concurrent.ArrayBlockingQueue; 12 import java.util.concurrent.BlockingQueue; 13 import java.util.concurrent.TimeUnit; 14  15 /** 16  * A {@link SocketWrapper} that also takes care of sending or responding to {@link HeartBeatMessage}, keeping the connection 17  * active so long as the endpoint proves to be active 18  */ 19 public class KeepAliveSocketWrapper extends SocketWrapper { 20  private final BlockingQueue<Message> inputQueue; 21  private final HeartBeatSender heartBeatSender; 22  private final long keepAlivePeriod; 23  private Timer heartBeatTimer; 24  25  /** 26  * Wraps the socket around a keep alive wrapper. The socket can be active or passive, the former means the socket 27  * actively sends a {@link HeartBeatMessage} at constant intervals, the latter means the socket sends a {@link HeartBeatMessage} 28  * for every {@link HeartBeatMessage} received. <br> 29  * The sockets will automatically close after a timeout, the timeout is 3 times the fixed duration used by an active 30  * wrapper to send its heartbeats. <br> 31  * Note: server used wrappers are encouraged to use the passive wrapper while clients are encouraged to use the active version 32  * instead. <br> 33  * Note: Assuming both ends of the connection use this wrapper keeping at least one active wrapper either as the client or 34  * server will keep the connection alive until an error occurs without the need to manually send the 35  * heartbeat. The same can be said if both ends of the connection are 36  * active wrappers. The same cannot be said if both ends are passive. 37  * 38  * @param socket the socket to wrap around 39  * @param keepAlivePeriod the time (in milliseconds) it takes for an active wrapper to send a heartbeat and a third of the 40  * timeout required to close the connection and deem the endpoint unreachable 41  * @param activeHeartBeat if set to true, makes the wrapper send a {@link HeartBeatMessage} every period, if set to false makes 42  * the {@link HeartBeatMessage} be sent only in response to a {@link HeartBeatMessage} in input. 43  * @throws IOException if any issues occur while wrapping the socket. 44  */ 45  public KeepAliveSocketWrapper(Socket socket, long keepAlivePeriod, boolean activeHeartBeat) throws IOException { 46  super(socket); 47  this.inputQueue = new ArrayBlockingQueue<>(10); 48  this.heartBeatSender = new HeartBeatSender(this); 49  this.heartBeatTimer = new Timer(); 50  this.keepAlivePeriod = keepAlivePeriod; 51  new Thread(() -> { 52  while (true) { 53  try { 54  Message input = super.awaitMessage(); 55  if (input instanceof HeartBeatMessage) { 56  this.heartBeatTimer.cancel(); 57  if (!activeHeartBeat) { 58  this.sendMessage(new HeartBeatMessage()); 59  } 60  this.heartBeatTimer = HeartBeatTimeoutTask.startAndGetTimer(this, keepAlivePeriod * 3); 61  } else { 62  inputQueue.put(input); 63  } 64  } catch (IOException | InterruptedException e) { 65  try { 66  this.close(); 67  } catch (IOException ex) { 68  throw new RuntimeException(ex); 69  } 70  this.heartBeatSender.cancel(); 71  Logger.info("closed KeepAliveSocketWrapper"); 72  return; 73  } 74  } 75  }).start(); 76  if (activeHeartBeat) { 77  this.heartBeatSender.start(keepAlivePeriod); 78  } 79  } 80  81  /** 82  * Blocks until a message is read or the heartbeat timeout expires 83  * 84  * @return the read message 85  * @throws IOException if the timeout runs out or if an error is encountered while fetching a message 86  */ 87  @Override 88  public Message awaitMessage() throws IOException { 89  try { 90  while (true) { 91  Message polledMessage = inputQueue.poll(keepAlivePeriod, TimeUnit.MILLISECONDS); 92  if (polledMessage != null) { 93  return polledMessage; 94  } else if (this.isClosed()) { 95  throw new SocketException("SocketWrapper is closed"); 96  } 97  } 98  } catch (InterruptedException e) { 99  throw new IOException(e); 100  } 101  } 102 }