/** * Add a node to the overlay network topology. * * @param data unused for this hash ring; nodes are placed evenly based on current topology * characteristics. You may safely pass 'null' to this method. * @return the location of the new node in the hash space. */ @Override public BigInteger addNode(T data) throws HashTopologyException, HashException { /* Edge case: when there are no entries in the hash ring yet. */ if (entryMap.values().size() == 0) { BigInteger pos; if (randomize) { /* Find a random location to start with */ pos = function.randomHash(); } else { pos = BigInteger.ZERO; } HashRingEntry firstEntry = new HashRingEntry(pos); entryMap.put(pos, firstEntry); return pos; } /* Edge case: only one entry in the hash ring */ if (entryMap.values().size() == 1) { HashRingEntry firstEntry = entryMap.values().iterator().next(); BigInteger halfSize = maxHash.divide(BigInteger.valueOf(2)); BigInteger secondPos = firstEntry.position.add(halfSize); if (secondPos.compareTo(maxHash) > 0) { secondPos = secondPos.subtract(maxHash); } HashRingEntry secondEntry = new HashRingEntry(secondPos, firstEntry); firstEntry.neighbor = secondEntry; entryMap.put(secondPos, secondEntry); return secondPos; } /* Find the largest empty span of hash space */ BigInteger largestSpan = BigInteger.ZERO; HashRingEntry largestEntry = null; for (HashRingEntry entry : entryMap.values()) { BigInteger len = lengthBetween(entry, entry.neighbor); if (len.compareTo(largestSpan) > 0) { largestSpan = len; largestEntry = entry; } } if (largestEntry == null) { return BigInteger.ONE.negate(); } /* Put the new node in the middle of the largest span */ BigInteger half = half(largestEntry, largestEntry.neighbor); addRingEntry(half, largestEntry); return half; }
/** * Insert a node into the hash ring internal data structures. Nodes are relinked with the proper * neighbors. * * @param position place in the hash space * @param predecessor the predecessor node in the hash space. */ private void addRingEntry(BigInteger position, HashRingEntry predecessor) throws HashTopologyException { if (entryMap.get(position) != null) { /* Something is already here! */ System.out.println(position); throw new HashTopologyException("Hash space exhausted!"); } HashRingEntry newEntry = new HashRingEntry(position, predecessor.neighbor); predecessor.neighbor = newEntry; entryMap.put(position, newEntry); }