/** * @author Joshua Kissoon * @created 20140218 * @desc Operation that handles connecting to an existing Kademlia network using a bootstrap node */ package socialkademlia.operation; import kademlia.message.Receiver; import java.io.IOException; import kademlia.KadConfiguration; import kademlia.KadServer; import kademlia.exceptions.RoutingException; import kademlia.message.AcknowledgeMessage; import kademlia.message.ConnectMessage; import kademlia.message.Message; import kademlia.node.Node; import kademlia.operation.Operation; import socialkademlia.SocialKademliaNode; public class ConnectOperation implements Operation, Receiver { public static final int MAX_CONNECT_ATTEMPTS = 5; // Try 5 times to connect to a node private final KadServer server; private final SocialKademliaNode localNode; private final Node bootstrapNode; private final KadConfiguration config; private boolean error; private int attempts; /** * @param server The message server used to send/receive messages * @param local The local node * @param bootstrap Node to use to bootstrap the local node onto the network * @param config */ public ConnectOperation(KadServer server, SocialKademliaNode local, Node bootstrap, KadConfiguration config) { this.server = server; this.localNode = local; this.bootstrapNode = bootstrap; this.config = config; } @Override public synchronized void execute() throws IOException { try { /* Contact the bootstrap node */ this.error = true; this.attempts = 0; Message m = new ConnectMessage(this.localNode.getNode()); /* Send a connect message to the bootstrap node */ server.sendMessage(this.bootstrapNode, m, this); /* If we haven't finished as yet, wait for a maximum of config.operationTimeout() time */ int totalTimeWaited = 0; int timeInterval = 50; // We re-check every 300 milliseconds while (totalTimeWaited < this.config.operationTimeout()) { if (error) { wait(timeInterval); totalTimeWaited += timeInterval; } else { break; } } if (error) { /* If we still haven't received any responses by then, do a routing timeout */ throw new RoutingException("ConnectOperation: Bootstrap node did not respond: " + bootstrapNode); } /* Perform lookup for our own ID to get nodes close to us */ Operation lookup = new NodeLookupOperation(this.server, this.localNode, this.localNode.getNode().getNodeId(), this.config); lookup.execute(); /** * Refresh buckets to get a good routing table * After the above lookup operation, K nodes will be in our routing table, * Now we try to populate all of our buckets. */ new BucketRefreshOperation(this.server, this.localNode, this.config).execute(); } catch (InterruptedException e) { System.err.println("Connect operation was interrupted. "); } } /** * Receives an AcknowledgeMessage from the bootstrap node. * * @param comm */ @Override public synchronized void receive(Message incoming, int comm) { /* The incoming message will be an acknowledgement message */ AcknowledgeMessage msg = (AcknowledgeMessage) incoming; /* The bootstrap node has responded, insert it into our space */ this.localNode.getRoutingTable().insert(this.bootstrapNode); /* We got a response, so the error is false */ error = false; /* Wake up any waiting thread */ notify(); } /** * Resends a ConnectMessage to the boot strap node a maximum of MAX_ATTEMPTS * times. * * @param comm * * @throws java.io.IOException */ @Override public synchronized void timeout(int comm) throws IOException { if (++this.attempts < MAX_CONNECT_ATTEMPTS) { this.server.sendMessage(this.bootstrapNode, new ConnectMessage(this.localNode.getNode()), this); } else { /* We just exit, so notify all other threads that are possibly waiting */ notify(); } } }