/*
* Copyright LGPL3
* YES Technology Association
* http://yestech.org
*
* http://www.opensource.org/licenses/lgpl-3.0.html
*/
package org.yestech.lib.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* A simple Consistent Hash Algorithmn derived from:
* <a href="http://weblogs.java.net/blog/tomwhite/archive/2007/11/consistent_hash.html">http://weblogs.java.net/blog/tomwhite/archive/2007/11/consistent_hash.html</a>
*
* @param <T> Type of the Node on the circle
*/
public class ConsistentHash<T> implements IConsistentHash<T> {
private final HashAlgorithm hashFunction;
private final int numberOfReplicas;
private final SortedMap<Long, T> circle = new TreeMap<Long, T>();
/**
* Creates a Ring and adds an initial collection of node to the Ring.
*
* @param hashFunction The Hashing Functions to use.
* @param numberOfReplicas Number of times to replicate a node on the Ring
*/
public ConsistentHash(HashAlgorithm hashFunction, int numberOfReplicas) {
this(hashFunction, numberOfReplicas, new ArrayList<T>());
}
/**
* Creates a Ring and adds an initial collection of node to the Ring.
*
* @param hashFunction The Hashing Functions to use.
* @param numberOfReplicas Number of times to replicate a node on the Ring
* @param nodes Nodes to add
*/
public ConsistentHash(HashAlgorithm hashFunction, int numberOfReplicas,
Collection<T> nodes) {
this.hashFunction = hashFunction;
this.numberOfReplicas = numberOfReplicas;
for (T node : nodes) {
add(node);
}
}
/**
* Add a node to the Ring and replicate it.
*
* @param node Node to add
*/
public void add(T node) {
for (int i = 0; i < numberOfReplicas; i++) {
circle.put(hashFunction.hash(node.toString() + i), node);
}
}
/**
* Remove a node from the Ring.
*
* @param node Node to remove
*/
public void remove(T node) {
for (int i = 0; i < numberOfReplicas; i++) {
circle.remove(hashFunction.hash(node.toString() + i));
}
}
/**
* Return the number of nodes on the Ring.
*
* @return the total node count.
*/
public int getTotalNodes() {
return circle.size();
}
/**
* Return the nearest node associated with a resource.
*
* @param resource The check to has to find the nearest node
* @return The nearest Node
*/
public T get(Object resource) {
if (circle.isEmpty()) {
return null;
}
long hash = hashFunction.hash(resource.toString());
if (!circle.containsKey(hash)) {
SortedMap<Long, T> tailMap = circle.tailMap(hash);
hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
}
return circle.get(hash);
}
}