/** Returns whether or not the local Node is in the given List */ private boolean containsNodeID(Collection<Contact> nodes, KUID id) { for (Contact node : nodes) { if (id.equals(node.getNodeID())) { return true; } } return false; }
@SuppressWarnings({"InfiniteLoopStatement"}) public static void main(String[] args) throws Exception { ArcsVisualizer arcs = new ArcsVisualizer(null, KUID.createRandomID()); JFrame frame = new JFrame(); frame.getContentPane().add(arcs); frame.setBounds(20, 30, 640, 480); frame.setVisible(true); Random generator = new Random(); EventType type = null; KUID nodeId = null; final int sleep = 100; while (true) { // Simulate an received or sent message nodeId = KUID.createRandomID(); if (generator.nextBoolean()) { type = EventType.MESSAGE_RECEIVED; } else { type = EventType.MESSAGE_SENT; } OpCode opcode = OpCode.valueOf(1 + generator.nextInt(OpCode.values().length - 1)); // OpCode opcode = OpCode.FIND_NODE_REQUEST; arcs.painter.handle(type, nodeId, null, opcode, true); // Sleep a bit... // Thread.sleep(sleep); Thread.sleep(generator.nextInt(sleep)); // Send every now an then a response if (generator.nextBoolean()) { if (type.equals(EventType.MESSAGE_SENT)) { arcs.painter.handle(EventType.MESSAGE_RECEIVED, nodeId, null, opcode, false); } else { arcs.painter.handle(EventType.MESSAGE_SENT, nodeId, null, opcode, false); } } } }
/** * Determines whether to remove, forward or to do nothing with the value that is associated with * the given valueId. */ private Operation getOperation(Contact node, Contact existing, KUID valueId) { // To avoid redundant STORE forward, a node only transfers a value // if it is the closest to the key or if its ID is closer than any // other ID (except the new closest one of course) // TODO: maybe relax this a little bit: what if we're not the closest // and the closest is stale? int k = KademliaSettings.REPLICATION_PARAMETER.getValue(); RouteTable routeTable = context.getRouteTable(); List<Contact> nodes = org.limewire.collection.CollectionUtils.toList( routeTable.select(valueId, k, SelectMode.ALL)); Contact closest = nodes.get(0); Contact furthest = nodes.get(nodes.size() - 1); if (LOG.isDebugEnabled()) { LOG.debug( MessageFormat.format( "node: {0}, existing: {1}, close nodes: {2}", node, existing, nodes)); } // StringBuilder sb = new StringBuilder(); // sb.append("ME: "+context.getLocalNode()+"\n"); // sb.append("Them: "+node).append("\n"); // sb.append("RT nearest: " + closest).append("\n"); // sb.append("RT furthest: " + furthest).append("\n"); // sb.append(CollectionUtils.toString(nodes)).append("\n"); // We store forward if: // #1 We're the nearest Node of the k-closest Nodes to // the given valueId // // #2 We're the second nearest of the k-closest Nodes to // the given valueId AND the other Node is the nearest. // In other words it changed its instance ID 'cause it // was offline for a short period of time or whatsoever. // (see also pre-condition(s) from where we're calling // this method) // // The first condition applies if the Node is new // and we're the closest Node. The second condition // applies if the Node has changed it's instanceId. // That means we're the second closest and since // the other Node has changed its instanceId we must // re-send the values if (context.isLocalNode(closest) || (node.equals(closest) && nodes.size() > 1 && context.isLocalNode(nodes.get(1)))) { KUID nodeId = node.getNodeID(); KUID furthestId = furthest.getNodeID(); // #3 The other Node must be equal to the furthest Node // or better if (nodeId.equals(furthestId) || nodeId.isNearerTo(valueId, furthestId)) { // sb.append("CONDITION B (FORWARD)").append("\n"); // sb.append("Local (from): " + context.getLocalNode()).append("\n"); // sb.append("Remote (to): " + node).append("\n"); // sb.append(CollectionUtils.toString(nodes)).append("\n"); // System.out.println(sb.toString()); if (LOG.isTraceEnabled()) { LOG.trace( "Node " + node + " is now close enough to a value and we are responsible for xfer"); } return Operation.FORWARD; } // We remove a value if: // #1 The value is stored at k Nodes // (i.e. the total number of Nodes in the DHT // is equal or greater than k. If the DHT has // less than k Nodes then there's no reason to // remove a value) // // #2 This Node is the furthest of the k-closest Nodes // // #3 The new Node isn't in our RouteTable yet. That means // adding it will push this Node out of the club of the // k-closest Nodes and makes it the (k+1)-closest Node. // // #4 The new Node is nearer to the given valueId then // the furthest away Node (we). } else if (nodes.size() >= k && context.isLocalNode(furthest) && (existing == null || existing.isDead())) { KUID nodeId = node.getNodeID(); KUID furthestId = furthest.getNodeID(); if (nodeId.isNearerTo(valueId, furthestId)) { // sb.append("CONDITION C").append("\n"); // sb.append("ME:").append(context.getLocalNode()).append("\n"); // sb.append("VALUE:").append(valueId).append("\n"); // sb.append("NODE:").append(node).append("\n"); // sb.append(CollectionUtils.toString(nodes)).append("\n"); // System.out.println(sb.toString()); return Operation.DELETE; } } return Operation.NOTHING; }
/** * Creates and returns a local Contact. * * @param vendor our vendor ID * @param version the version * @param firewalled whether or not we're firewalled */ public static Contact createLocalContact(Vendor vendor, Version version, boolean firewalled) { return createLocalContact(vendor, version, KUID.createRandomID(), 0, firewalled); }
/** Reads a KUID from the InputStream. */ public KUID readKUID() throws IOException { return KUID.createWithInputStream(this); }