package de.tum.in.www1.jReto.routing; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Set; import java.util.UUID; import de.tum.in.www1.jReto.connectivity.PacketConnection; import de.tum.in.www1.jReto.module.api.Address; import de.tum.in.www1.jReto.module.api.Connection; import de.tum.in.www1.jReto.packet.Packet; import de.tum.in.www1.jReto.packet.PacketType; import de.tum.in.www1.jReto.routing.Router.OnConnectionHandler; import de.tum.in.www1.jReto.routing.Router.OnFailHandler; import de.tum.in.www1.jReto.routing.packets.ConnectionPurpose; /** * The Node class represents the routing component of a remote peer. It stores all routing related information about that peer. * Nodes are created and managed by a Router. * * Nodes also forward FloodPackets to the FloodingPacketManager which handles those packets. These packets are used to transmit routing information. */ public class Node implements PacketConnection.Handler { /** The Router that created this Node object*/ private final Router router; /** The Node's identifer*/ private final UUID identifier; /** The local peer's identifier. */ private final UUID localIdentifier; /** Addresses that allow to connect to this node directly. */ private final Set<Address> directAddresses = new HashSet<Address>(); /** Stores the PacketConnection used to transmit routing metadata. */ private PacketConnection routingConnection; /** The next hop to use when establishing a connection to this node (if the optimal route should be used). */ private Node nextHop; private int cost; /** Initializes a Node object */ public Node(Router router, UUID identifier, UUID localIdentifier) { this.router = router; this.identifier = identifier; this.localIdentifier = localIdentifier; } /** Whether this node is a neighbor of the local peer. */ public boolean isNeighbor() { return this.nextHop == this; } /** Whether a route to this node exists or not. */ public boolean isReachable() { return this.nextHop != null; } public Node getNextHop() { return this.nextHop; } public void setNextHop(Node node) { this.nextHop = node; } public int getCost() { return this.cost; } public void setCost(int cost) { this.cost = cost; } public UUID getIdentifier() { return this.identifier; } public PacketConnection getRoutingConnection() { return this.routingConnection; } public Set<Address> getAddresses() { return this.directAddresses; } public boolean isResponsibleForEstablishingRoutingConnection() { if (this.identifier.getMostSignificantBits() == this.localIdentifier.getMostSignificantBits()) { return this.identifier.getLeastSignificantBits() == this.localIdentifier.getLeastSignificantBits(); } else { return this.identifier.getMostSignificantBits() < this.localIdentifier.getMostSignificantBits(); } } public Address getBestAddress() { if (this.directAddresses.size() == 0) return null; return Collections.min(new ArrayList<Address>(this.directAddresses), new Comparator<Address>() { public int compare(Address o1, Address o2) { return o1.getCost() - o2.getCost(); } }); } public void connect(OnConnectionHandler onConnection, OnFailHandler onFail) { this.router.establishMulticastConnection(new HashSet<Node>(Arrays.asList(this)), onConnection, onFail); //this.router.establishRoutedConnection(this, onConnection, onFail); } public void establishRoutingConnection() { if (!this.isResponsibleForEstablishingRoutingConnection()) return; if (this.routingConnection != null && this.routingConnection.getIsConnected()) return; this.getBestAddress(); this.router.establishDirectConnection(this, ConnectionPurpose.ROUTING_DATA_EXCHANGE_CONNECTION, new OnConnectionHandler() { @Override public void onConnect(Connection connection) { PacketConnection packetConnection = new PacketConnection(connection, null, new HashSet<>(Arrays.asList(Node.this))); Node.this.setupRoutingConnection(packetConnection); } }, new OnFailHandler() { @Override public void onFail() { System.err.println("Failed to establish routing connection."); } }); } public void handleRoutingConnection(Connection connection) { PacketConnection packetConnection = new PacketConnection(connection, null, new HashSet<>(Arrays.asList(this))); this.setupRoutingConnection(packetConnection); } private void setupRoutingConnection(PacketConnection connection) { this.routingConnection = connection; connection.addDelegate(this); this.router.onNeighborReachable(this); if (connection.getIsConnected()) { this.onUnderlyingConnectionConnected(connection); } } public void closeRoutingConnection() { if (this.routingConnection == null) { return; } this.routingConnection.getUnderlyingConnection().close(); } public void setIncomingRoutingConnection(PacketConnection connection) { this.setupRoutingConnection(connection); } public void sendPacket(Packet packet) { this.routingConnection.writePacket(packet); } public UUID getLocalPeerIdentifier() { return this.localIdentifier; } public void addAddress(Address address) { this.directAddresses.add(address); } public void removeAddress(Address address) { this.directAddresses.remove(address); } @Override public void onUnderlyingConnectionClose(PacketConnection connection) { System.err.println("Lost routing connection: " + this); //ensure that the stale underlying connection is closed. Otherwise, this will result in one-way only traffic //between two peers. We will not be able to connect to a peer who had restarted via localpeer.stop() --> localPeer.Start() this.router.onNeighborLost(this); } @Override public void onWillSwapUnderlyingConnection(PacketConnection connection) {} @Override public void onUnderlyingConnectionConnected(PacketConnection connection) {} @Override public void onNoPacketsLeft(PacketConnection connection) {} @Override public Set<PacketType> getHandledPacketTypes() { return this.router.getLinkStatePacketManager().getHandledPacketTypes(); } @Override public void handlePacket(ByteBuffer data, PacketType type) { this.router.getLinkStatePacketManager().handlePacket(data, type, this.identifier); } }