/* * INESC-ID, Instituto de Engenharia de Sistemas e Computadores Investigação e Desevolvimento em Lisboa * Copyright 2013 INESC-ID and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt in the distribution for a full listing of * individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3.0 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.infinispan.dataplacement; import org.infinispan.commons.hash.Hash; import org.infinispan.remoting.transport.Address; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.Set; /** * Cluster Snapshot that gives a view of the cluster with the members. * This structure ensures the same index for the same address in every member of the cluster and * it has search complexity of n*log(n) (as in Arrays.binarySearch()) * * This collection does not allow duplicate addresses. * * @author Pedro Ruivo * @since 5.2 */ public class ClusterSnapshot { private static final InternalAddressComparator COMPARATOR = new InternalAddressComparator(); private final InternalAddress[] internalAddresses; private final Hash hashFunction; public ClusterSnapshot(Address[] members, Hash hashFunction) { this.hashFunction = hashFunction; Set<InternalAddress> unique = new HashSet<InternalAddress>(); for (Address address : members) { unique.add(new InternalAddress(address)); } internalAddresses = unique.toArray(new InternalAddress[unique.size()]); Arrays.sort(internalAddresses, COMPARATOR); } public ClusterSnapshot(Collection<Address> members, Hash hashFunction) { this.hashFunction = hashFunction; Set<InternalAddress> unique = new HashSet<InternalAddress>(); for (Address address : members) { unique.add(new InternalAddress(address)); } internalAddresses = unique.toArray(new InternalAddress[unique.size()]); Arrays.sort(internalAddresses, COMPARATOR); } /** * Returns the index of the address, or -1 if this collection does not contain the address. * * @param address the address * @return the index of the address, or -1 if this collection does not contain the address. */ public final int indexOf(Address address) { if (address == null) { return -1; } InternalAddress internalAddress = new InternalAddress(address); int index = Arrays.binarySearch(internalAddresses, internalAddress, COMPARATOR); if (index < 0) { return -1; } return checkIndex(index, internalAddress); } /** * returns the address in the position defined by {@code index} or null if the index is negative or higher than * the size of this collection * * @param index the index * @return the address in the position defined by {@code index} or null if the index is negative or higher * than the size of this collection */ public final Address get(int index) { if (index < 0 || index >= internalAddresses.length) { return null; } return internalAddresses[index].address; } /** * returns true if this collection contains the address, false otherwise. * More formally, returns true if indexOf(address) != -1 * * @param address the address * @return true if this collection contains the address, false otherwise. */ public final boolean contains(Address address) { return indexOf(address) != -1; } /** * returns the number of addresses in this collection * * @return the number of addresses in the collection */ public final int size() { return internalAddresses.length; } private int checkIndex(int index, InternalAddress internalAddress) { //first check the index returned by the binarySearch if (internalAddresses[index].address.equals(internalAddress.address)) { return index; } //second, check backwards int newIndex = index - 1; while (newIndex >= 0 && internalAddresses[newIndex].hashCode == internalAddress.hashCode) { if (internalAddresses[newIndex].address.equals(internalAddress.address)) { return newIndex; } newIndex--; } //finally, check forwards newIndex = index + 1; while (newIndex < internalAddresses.length && internalAddresses[newIndex].hashCode == internalAddress.hashCode) { if (internalAddresses[newIndex].address.equals(internalAddress.address)) { return newIndex; } newIndex++; } //it can be the case that the address has the same hash but it is not in the array return -1; } private class InternalAddress { private final Address address; private final int hashCode; private InternalAddress(Address address) { this.address = address; hashCode = Math.abs(hashFunction.hash(address)); } @Override public String toString() { return "InternalAddress{" + "address=" + address + ", hashCode=" + hashCode + '}'; } } private static class InternalAddressComparator implements Comparator<InternalAddress> { @Override public int compare(InternalAddress internalAddress1, InternalAddress internalAddress2) { return internalAddress1.hashCode - internalAddress2.hashCode; } } @Override public String toString() { return "ClusterSnapshot{" + "internalAddresses=" + (internalAddresses == null ? null : Arrays.asList(internalAddresses)) + '}'; } }