Adiciona gerenciamento de heartbeat e peers
This commit is contained in:
parent
d22b95e74f
commit
076b4fcb43
|
|
@ -0,0 +1,101 @@
|
||||||
|
package pitiupi.control;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import pitiupi.GUI.MainWindow;
|
||||||
|
import pitiupi.net.HeartbeatMessage;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author tony
|
||||||
|
*/
|
||||||
|
public class HeartbeatManager {
|
||||||
|
|
||||||
|
private static final long HEARTBEAT_INTERVAL_MS = 5000;
|
||||||
|
private static final long PEER_TIMEOUT_MS = 15000; // ~3 batimentos perdidos
|
||||||
|
|
||||||
|
private final MainWindow mainWindow;
|
||||||
|
private final Map<String, PeerInfo> peers = new ConcurrentHashMap<>();
|
||||||
|
private final List<PeerListener> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
private ScheduledExecutorService scheduler;
|
||||||
|
|
||||||
|
public HeartbeatManager(MainWindow mainWindow) {
|
||||||
|
this.mainWindow = mainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
scheduler = Executors.newScheduledThreadPool(1);
|
||||||
|
scheduler.scheduleAtFixedRate(this::announce, 0, HEARTBEAT_INTERVAL_MS, TimeUnit.MILLISECONDS);
|
||||||
|
scheduler.scheduleAtFixedRate(this::checkTimeouts, HEARTBEAT_INTERVAL_MS, HEARTBEAT_INTERVAL_MS, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
if (scheduler != null) {
|
||||||
|
scheduler.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void announce() {
|
||||||
|
try {
|
||||||
|
HeartbeatMessage msg = new HeartbeatMessage(mainWindow.getUserName());
|
||||||
|
mainWindow.getSocket().send(msg.toByteArray());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.getLogger(HeartbeatManager.class.getName()).log(Level.WARNING, "Falha ao enviar heartbeat", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chamado pelo Socket quando um pacote é identificado como heartbeat
|
||||||
|
public void receiveHeartbeat(HeartbeatMessage msg, InetAddress from) {
|
||||||
|
// ignora o próprio heartbeat
|
||||||
|
if (msg.getUserName().equals(mainWindow.getUserName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = from.getHostAddress() + "#" + msg.getUserName();
|
||||||
|
boolean isNew = !peers.containsKey(key);
|
||||||
|
|
||||||
|
PeerInfo peer = peers.computeIfAbsent(key, k -> new PeerInfo(msg.getUserName(), from, msg.getTimestamp()));
|
||||||
|
peer.touch(System.currentTimeMillis());
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkTimeouts() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
boolean changed = peers.values().removeIf(p -> now - p.getLastSeen() > PEER_TIMEOUT_MS);
|
||||||
|
if (changed) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyListeners() {
|
||||||
|
List<PeerInfo> snapshot = new ArrayList<>(peers.values());
|
||||||
|
for (PeerListener l : listeners) {
|
||||||
|
l.onPeersChanged(snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPeerListener(PeerListener l) {
|
||||||
|
listeners.add(l);
|
||||||
|
l.onPeersChanged(new ArrayList<>(peers.values()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePeerListener(PeerListener l) {
|
||||||
|
listeners.remove(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PeerInfo> getPeers() {
|
||||||
|
return new ArrayList<>(peers.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package pitiupi.control;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author tony
|
||||||
|
*/
|
||||||
|
public class PeerInfo {
|
||||||
|
|
||||||
|
private final String userName;
|
||||||
|
private final InetAddress address;
|
||||||
|
private volatile long lastSeen;
|
||||||
|
|
||||||
|
public PeerInfo(String userName, InetAddress address, long lastSeen) {
|
||||||
|
this.userName = userName;
|
||||||
|
this.address = address;
|
||||||
|
this.lastSeen = lastSeen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InetAddress getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastSeen() {
|
||||||
|
return lastSeen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void touch(long timestamp) {
|
||||||
|
this.lastSeen = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return userName + " (" + address.getHostAddress() + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package pitiupi.control;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author tony
|
||||||
|
*/
|
||||||
|
public interface PeerListener {
|
||||||
|
void onPeersChanged(List<PeerInfo> peers);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package pitiupi.net;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author tony
|
||||||
|
*/
|
||||||
|
public class HeartbeatMessage extends Message {
|
||||||
|
|
||||||
|
private final String userName;
|
||||||
|
private final long timestamp;
|
||||||
|
|
||||||
|
public HeartbeatMessage(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue