package de.tum.in.www1.jReto.routing.packets;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import de.tum.in.www1.jReto.packet.Constants;
import de.tum.in.www1.jReto.packet.DataChecker;
import de.tum.in.www1.jReto.packet.DataReader;
import de.tum.in.www1.jReto.packet.DataWriter;
import de.tum.in.www1.jReto.packet.Packet;
import de.tum.in.www1.jReto.packet.PacketType;
import de.tum.in.www1.jReto.routing.algorithm.Tree;
/**
* The MulticastHandshake contains information relevant to establish a routed multi- or unicast connection.
* It is sent to each peer that is part of the route.
* It contains the identifier of the peer that originally established the peer, the set of destinations of the connection, and the direct connections that
* still need to be established structured as a tree (the nextHopTree). When a peer receives a MulticastHandshake, the nextHopTree is always rooted at that tree.
* That node is expected to establish connections to all nodes that are its children in the nextHopTree.
*/
public class MulticastHandshake implements Packet {
public final static PacketType TYPE = PacketType.ROUTING_HANDSHAKE;
public final static int MINIMUM_LENGTH = Constants.PACKET_TYPE_SIZE + Constants.UUID_SIZE;
public final UUID sourcePeerIdentifier;
public final Set<UUID> destinationIdentifiers;
public final Tree<UUID> nextHopsTree;
public MulticastHandshake(UUID sourcePeerIdentifier, Set<UUID> destinationIdentifiers, Tree<UUID> nextHopsTree) {
if (destinationIdentifiers.size() == 0) throw new IllegalArgumentException("At least one destination is required.");
this.sourcePeerIdentifier = sourcePeerIdentifier;
this.destinationIdentifiers = destinationIdentifiers;
this.nextHopsTree = nextHopsTree;
}
public static MulticastHandshake deserialize(ByteBuffer data) {
DataReader reader = new DataReader(data);
if (!DataChecker.check(reader, TYPE, MINIMUM_LENGTH)) return null;
UUID sourcePeerIdentifier = reader.getUUID();
int destinationsCount = reader.getInt();
if (destinationsCount == 0) {
System.err.println("Invalid MulticastHandshake: No destinations specified.");
return null;
}
if (!reader.checkRemaining(destinationsCount * Constants.UUID_SIZE)) {
System.err.println("Invalid MulticastHandshake: Not enough data remaining to read destinations.");
return null;
}
Set<UUID> destinations = new HashSet<>();
for (int i=0; i<destinationsCount; i++) {
destinations.add(reader.getUUID());
}
Tree<UUID> nextHopsTree = deserializeNextHopTree(reader);
return new MulticastHandshake(sourcePeerIdentifier, destinations, nextHopsTree);
}
public ByteBuffer serialize() {
DataWriter data = new DataWriter(MINIMUM_LENGTH + Constants.INT_SIZE + destinationIdentifiers.size() * Constants.UUID_SIZE + nextHopsTree.size() * (Constants.INT_SIZE + Constants.UUID_SIZE));
data.add(TYPE);
data.add(this.sourcePeerIdentifier);
data.add(destinationIdentifiers.size());
for (UUID destinationIdentifier : destinationIdentifiers) data.add(destinationIdentifier);
serializeNextHopTree(data, nextHopsTree);
return data.getData();
}
private static Tree<UUID> deserializeNextHopTree(DataReader reader) {
if (!reader.checkRemaining(Constants.UUID_SIZE + Constants.INT_SIZE)) {
System.err.println("Invalid MulticastHandshake: Not enough data remaining to read hop tree.");
return null;
}
UUID value = reader.getUUID();
int childrenCount = reader.getInt();
Set<Tree<UUID>> children = new HashSet<>();
for (int i=0; i<childrenCount; i++) {
Tree<UUID> child = deserializeNextHopTree(reader);
if (child == null) return null;
children.add(child);
}
return new Tree<>(value, children);
}
private void serializeNextHopTree(DataWriter data, Tree<UUID> tree) {
data.add(tree.value);
data.add(tree.children.size());
for (Tree<UUID> child : tree.children) {
serializeNextHopTree(data, child);
}
}
}