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 }