Beispiel #1
0
  @Override
  @VarZ("/dht/messages/incoming")
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws IOException, ServletException {
    int length = request.getContentLength();
    byte[] data = new byte[length];
    DataInputStream dataIs = new DataInputStream(request.getInputStream());
    dataIs.readFully(data);
    dataIs.close();

    String hostname = request.getHeader("X-Node-Host");
    String port = request.getHeader("X-Node-Port");

    InetSocketAddress src = InetSocketAddress.createUnresolved(hostname, Integer.valueOf(port));

    try {
      DHTMessage destination =
          context.getMessageFactory().createMessage(src, ByteBuffer.wrap(data));

      varZ.log("/dht/messages/incoming/" + destination.getOpCode().name().toLowerCase());

      dispatcher.get().handleMessage(destination);
    } catch (Exception e) {
      logger.error(e);
    }
  }
  public void handleLateResponse(ResponseMessage message) {
    Contact node = message.getContact();

    if (!node.isFirewalled()) {
      context.getRouteTable().add(node); // update
    }
  }
  /**
   * This method depends on addLiveContactInfo(...) and does two things. It either forwards or
   * removes a DHTValue it from the local Database. For details see Kademlia spec!
   */
  private void forwardOrRemoveValues(Contact node, Contact existing, DHTMessage message) {

    List<DHTValueEntity> valuesToForward = new ArrayList<DHTValueEntity>();

    Database database = context.getDatabase();
    synchronized (database) {
      for (KUID primaryKey : database.keySet()) {

        Operation op = getOperation(node, existing, primaryKey);

        if (LOG.isDebugEnabled())
          LOG.debug("node: " + node + "existing: " + existing + "operation: " + op);

        if (op.equals(Operation.FORWARD)) {
          Map<KUID, DHTValueEntity> bag = database.get(primaryKey);
          valuesToForward.addAll(bag.values());
          databaseStats.STORE_FORWARD_COUNT.incrementStat();

        } else if (op.equals(Operation.DELETE)
            && DatabaseSettings.DELETE_VALUE_IF_FURTHEST_NODE.getValue()) {
          Map<KUID, DHTValueEntity> bag = database.get(primaryKey);
          for (DHTValueEntity entity : bag.values()) {
            // System.out.println("REMOVING: " + entity + "\n");
            database.remove(entity.getPrimaryKey(), entity.getSecondaryKey());
          }
          databaseStats.STORE_FORWARD_REMOVALS.incrementStat();
        }
      }
    }

    if (!valuesToForward.isEmpty()) {
      SecurityToken securityToken = null;
      if (message instanceof SecurityTokenProvider) {
        securityToken = ((SecurityTokenProvider) message).getSecurityToken();

        if (securityToken == null && StoreSettings.STORE_REQUIRES_SECURITY_TOKEN.getValue()) {
          if (LOG.isInfoEnabled()) {
            LOG.info(node + " sent us a null SecurityToken");
          }
          return;
        }
      }

      context.store(node, securityToken, valuesToForward);
    }
  }
Beispiel #4
0
  @Test
  public void testSettingTheBootstrapNodeAsBootStrapped() throws Exception {
    Context dht = (Context) MojitoFactory.createDHT("bootstrap");
    dht.bind(new InetSocketAddress("localhost", 8080));
    dht.setBootstrapped(true);
    dht.start();

    MojitoDHT node = MojitoFactory.createDHT("node");
    node.bind(new InetSocketAddress("localhost", 8081));
    node.start();
    node.bootstrap(new InetSocketAddress("localhost", 8080)).get();
    assertTrue(node.isBootstrapped());

    DHTValueImpl value =
        new DHTValueImpl(DHTValueType.TEXT, Version.ZERO, "hello world".getBytes());

    node.put(Keys.of("key"), value).get();

    FindValueResult result =
        dht.get(EntityKey.createEntityKey(Keys.of("key"), DHTValueType.TEXT)).get();

    assertTrue(result.isSuccess());
    assertEquals(1, result.getEntities().size());
    assertEquals(
        "hello world", new String(result.getEntities().iterator().next().getValue().getValue()));

    node.close();
    dht.close();
  }
  public static ArcsVisualizer show(final Context context) {
    final ArcsVisualizer arcs = new ArcsVisualizer(context, context.getLocalNodeID());

    SwingUtilities.invokeLater(
        new Runnable() {
          public void run() {
            JFrame frame = new JFrame(context.getName());
            frame.getContentPane().add(arcs);
            frame.setBounds(20, 30, 640, 640);
            frame.addWindowListener(
                new WindowAdapter() {
                  @Override
                  public void windowClosing(WindowEvent e) {
                    arcs.stopArcs();
                  }
                });
            frame.setVisible(true);
          }
        });

    arcs.startArcs();
    return arcs;
  }
 public void handleTimeout(KUID nodeId, SocketAddress dst, RequestMessage message, long time) {
   context.getRouteTable().handleFailure(nodeId, dst);
 }
  public DefaultMessageHandler(Context context) {
    this.context = context;

    databaseStats = context.getDatabaseStats();
  }
  /**
   * 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;
  }
  /**
   * Adds the given <code>Contact</code> or updates it if it's already in our <code>RouteTable
   * </code>
   */
  private synchronized void addLiveContactInfo(Contact node, DHTMessage message) {

    RouteTable routeTable = context.getRouteTable();

    // If the Node is going to shutdown then don't bother
    // further than this.
    if (node.isShutdown()) {
      if (LOG.isInfoEnabled()) {
        LOG.info(node + " is going to shut down");
      }

      synchronized (routeTable) {
        // Make sure there's an existing Contact in the RouteTable.
        // Otherwise don't bother!
        Contact existing = routeTable.get(node.getNodeID());
        if (node.equals(existing)) {

          // Update the new Contact in the RouteTable and
          // mark it as shutdown
          // mark the existing contact as shutdown if its alive or
          // it will not be removed.
          if (existing.isAlive()) existing.shutdown(true);
          routeTable.add(node);
          node.shutdown(true);
        }
      }
      return;
    }

    // Ignore firewalled Nodes
    if (node.isFirewalled()) {
      if (LOG.isInfoEnabled()) {
        LOG.info(node + " is firewalled");
      }
      return;
    }

    if (ContactUtils.isPrivateAddress(node)) {
      if (LOG.isInfoEnabled()) {
        LOG.info(node + " has a private address");
      }
      return;
    }

    KUID nodeId = node.getNodeID();
    if (context.isLocalNodeID(nodeId)) {

      // This is expected if there's a Node ID collision
      assert (message instanceof PingResponse)
          : "Expected a PingResponse but got a "
              + message.getClass()
              + " from "
              + message.getContact();

      if (LOG.isInfoEnabled()) {
        LOG.info("Looks like our NodeID collides with " + node);
      }

      return;
    }

    if (StoreSettings.STORE_FORWARD_ENABLED.getValue()) {
      // Only do store forward if it is a new node in our routing table
      // (we are (re)connecting to the network) or a node that is reconnecting
      Contact existing = routeTable.get(nodeId);

      if (existing == null
          || existing.isDead()
          || existing.getInstanceID() != node.getInstanceID()) {

        // Store forward only if we're bootstrapped
        if (context.isBootstrapped()) {
          int k = KademliaSettings.REPLICATION_PARAMETER.getValue();
          // we select the 2*k closest nodes in order to also check those values
          // where the local node is part of the k closest to the value but not part
          // of the k closest to the new joining node.
          Collection<Contact> nodes = routeTable.select(nodeId, 2 * k, SelectMode.ALL);

          // Are we one of the K nearest Nodes to the contact?
          if (containsNodeID(nodes, context.getLocalNodeID())) {
            if (LOG.isTraceEnabled()) {
              LOG.trace(
                  "Node "
                      + node
                      + " is new or has changed his instanceID, will check for store forward!");
            }

            forwardOrRemoveValues(node, existing, message);
          }
        }
      }
    }

    // Add the Node to our RouteTable or if it's
    // already there update its timeStamp and whatsoever
    routeTable.add(node);
  }
 public void stopArcs() {
   timer.stop();
   context.getMessageDispatcher().removeMessageDispatcherListener(this);
 }
 public void startArcs() {
   timer.start();
   context.getMessageDispatcher().addMessageDispatcherListener(this);
 }
Beispiel #12
0
  @Test
  public void testCreatorAddressIsCorrect() throws Exception {
    Context root = (Context) MojitoFactory.createDHT("bootstrap");
    root.bind(new InetSocketAddress("localhost", 8081));
    root.start();

    Context dht = (Context) MojitoFactory.createDHT("dht");
    dht.bind(new InetSocketAddress("localhost", 8080));
    dht.start();
    dht.bootstrap(new InetSocketAddress("localhost", 8081)).get();
    assertTrue(dht.isBootstrapped());

    DHTValueImpl value =
        new DHTValueImpl(DHTValueType.TEXT, Version.ZERO, "hello world".getBytes());

    StoreResult store = dht.put(Keys.of("key"), value).get();

    assertEquals(2, store.getLocations().size());

    FindValueResult result =
        dht.get(EntityKey.createEntityKey(Keys.of("key"), DHTValueType.TEXT)).get();

    assertTrue(result.isSuccess());
    assertEquals(1, result.getEntities().size());
    DHTValueEntity entity = result.getEntities().iterator().next();
    assertEquals(
        InetSocketAddress.createUnresolved("localhost", 8080),
        entity.getCreator().getContactAddress());
    assertEquals(
        InetSocketAddress.createUnresolved("localhost", 8081),
        entity.getSender().getContactAddress());

    root.close();
    dht.close();
  }