package org.infinispan.distribution;
import org.infinispan.remoting.transport.Address;
import java.util.ArrayList;
import java.util.Arrays;
import org.infinispan.marshall.Ids;
import java.io.IOException;
import java.util.List;
import org.infinispan.marshall.Marshallable;
import static java.lang.Math.min;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Adapter around a generic consistant hash to allow hashing of jgroups addresses.
*/
@Marshallable(externalizer = DefaultConsistentHash.Externalizer.class, id = Ids.DEFAULT_CONSISTENT_HASH)
public class AddressHash extends AbstractConsistentHash {
/** A Weight and weight factor of 1 gives one node per address. */
private static final int WEIGHT = 1;
/** Weightfactor and weight control the number of virtual nodes in the consistent hash. */
private static final int WEIGHT_FACTOR = 1;
// make sure all threads see the current list
ArrayList
addresses;
GenericConsistentHash chash;
public void setCaches(List caches) {
addresses = new ArrayList(caches.size());
Address[] rawAddresses = caches.toArray(new Address[caches.size()]);
int[] weights = new int[rawAddresses.length];
Arrays.fill(weights, WEIGHT);
// A consistent hash of the addresses, with no virtual nodes.
chash = new GenericConsistentHash.Builder(rawAddresses)
.weights(weights)
.weightFactor(WEIGHT_FACTOR).build();
int poolSize = chash.getPoolSize();
Address[] hashedAddresses = new Address[poolSize];
hashedAddresses = chash.getPoolValues(hashedAddresses);
for (int i=0; i getCaches() {
return addresses;
}
public List locate(Object key, int replCount) {
int clusterSize = addresses.size();
int numCopiesToFind = min(replCount, clusterSize);
// Loads the primary owner of the key, and replication servers into the owners array.
Address[] owners = chash.locate(key, new Address[numCopiesToFind], numCopiesToFind);
return Arrays.asList(owners);
}
/**
* The distance between the first entries in the address array for two caches, a1 and a2.
* Of questionable use when virtual nodes are employed.
*
* @param a1 The address of the first cache.
* @param a2 The address of the second cache.
*
* @return Am int containing the difference between these two indices.
*/
public int getDistance(Address a1, Address a2) {
if (a1 == null || a2 == null) throw new NullPointerException("Cannot find the distance between null servers.");
int p1 = addresses.indexOf(a1);
if (p1 < 0)
throw new IllegalArgumentException("Address " + a1 + " not in the addresses list of this consistent hash impl!");
int p2 = addresses.indexOf(a2);
if (p2 < 0)
throw new IllegalArgumentException("Address " + a2 + " not in the addresses list of this consistent hash impl!");
if (p1 <= p2)
return p2 - p1;
else
return addresses.size() - (p1 - p2);
}
/**
* Two hashes are adjacent if they are next to each other in the consistent hash.
* @param a1 The address of the first cache.
* @param a2 The address of the second cache.
*
* @return A boolean, true if they are adjacent, false if not.
*/
public boolean isAdjacent(Address a1, Address a2) {
int distance = getDistance(a1, a2);
return distance == 1 || distance == addresses.size() - 1;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AddressHash that = (AddressHash) o;
if (addresses != null ? !addresses.equals(that.addresses) : that.addresses != null) return false;
if (chash != null ? !chash.equals(that.chash) : that.chash != null) return false;
return true;
}
@Override
public int hashCode() {
int result = addresses != null ? addresses.hashCode() : 0;
result = 31 * result + (chash != null ? chash.hashCode() : 0);
return result;
}
public static class Externalizer implements org.infinispan.marshall.Externalizer {
public void writeObject(ObjectOutput output, Object subject) throws IOException {
DefaultConsistentHash dch = (DefaultConsistentHash) subject;
output.writeObject(dch.addresses);
output.writeObject(dch.positions);
}
@SuppressWarnings("unchecked")
public Object readObject(ObjectInput unmarshaller) throws IOException, ClassNotFoundException {
AddressHash dch = new AddressHash();
dch.addresses = (ArrayList) unmarshaller.readObject();
dch.chash = (GenericConsistentHash) unmarshaller.readObject();
return dch;
}
}
}