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