/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hazelcast.internal.cluster.impl; import com.hazelcast.core.Member; import com.hazelcast.instance.MemberImpl; import com.hazelcast.nio.Address; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import static java.util.Collections.singletonMap; import static java.util.Collections.unmodifiableCollection; /** * A special, immutable {@link MemberImpl} map type, * that allows querying members using address or uuid. */ final class MemberMap { private final int version; private final Map<Address, MemberImpl> addressToMemberMap; private final Map<String, MemberImpl> uuidToMemberMap; private final Set<MemberImpl> members; MemberMap(int version, Map<Address, MemberImpl> addressMap, Map<String, MemberImpl> uuidMap) { this.version = version; assert new HashSet<MemberImpl>(addressMap.values()).equals(new HashSet<MemberImpl>(uuidMap.values())) : "Maps are different! AddressMap: " + addressMap + ", UuidMap: " + uuidMap; this.addressToMemberMap = addressMap; this.uuidToMemberMap = uuidMap; this.members = Collections.unmodifiableSet(new LinkedHashSet<MemberImpl>(addressToMemberMap.values())); } /** * Creates an empty {@code MemberMap}. * * @return empty {@code MemberMap} */ static MemberMap empty() { return new MemberMap(0, Collections.<Address, MemberImpl>emptyMap(), Collections.<String, MemberImpl>emptyMap()); } /** * Creates a singleton {@code MemberMap} including only specified member. * * @param member sole member in map * @return singleton {@code MemberMap} */ static MemberMap singleton(MemberImpl member) { return new MemberMap(1, singletonMap(member.getAddress(), member), singletonMap(member.getUuid(), member)); } /** * Creates a new {@code MemberMap} including given members. * * @param members members * @return a new {@code MemberMap} */ static MemberMap createNew(MemberImpl... members) { return createNew(0, members); } /** * Creates a new {@code MemberMap} including given members. * * @param version version * @param members members * @return a new {@code MemberMap} */ static MemberMap createNew(int version, MemberImpl... members) { Map<Address, MemberImpl> addressMap = new LinkedHashMap<Address, MemberImpl>(); Map<String, MemberImpl> uuidMap = new LinkedHashMap<String, MemberImpl>(); for (MemberImpl member : members) { putMember(addressMap, uuidMap, member); } return new MemberMap(version, addressMap, uuidMap); } /** * Creates clone of source {@code MemberMap}, excluding given members. * If source is empty, same map instance will be returned. If excluded members are empty or not present in * source, a new map will be created containing the same members with source. * * @param source source map * @param excludeMembers members to exclude * @return clone map */ static MemberMap cloneExcluding(MemberMap source, MemberImpl... excludeMembers) { if (source.size() == 0) { return source; } Map<Address, MemberImpl> addressMap = new LinkedHashMap<Address, MemberImpl>(source.addressToMemberMap); Map<String, MemberImpl> uuidMap = new LinkedHashMap<String, MemberImpl>(source.uuidToMemberMap); for (MemberImpl member : excludeMembers) { MemberImpl removed = addressMap.remove(member.getAddress()); if (removed != null) { uuidMap.remove(removed.getUuid()); } removed = uuidMap.remove(member.getUuid()); if (removed != null) { addressMap.remove(removed.getAddress()); } } return new MemberMap(source.version + 1, addressMap, uuidMap); } /** * Creates clone of source {@code MemberMap} additionally including new members. * * @param source source map * @param newMembers new members to add * @return clone map */ static MemberMap cloneAdding(MemberMap source, MemberImpl... newMembers) { Map<Address, MemberImpl> addressMap = new LinkedHashMap<Address, MemberImpl>(source.addressToMemberMap); Map<String, MemberImpl> uuidMap = new LinkedHashMap<String, MemberImpl>(source.uuidToMemberMap); for (MemberImpl member : newMembers) { putMember(addressMap, uuidMap, member); } return new MemberMap(source.version + 1, addressMap, uuidMap); } private static void putMember(Map<Address, MemberImpl> addressMap, Map<String, MemberImpl> uuidMap, MemberImpl member) { MemberImpl current = addressMap.put(member.getAddress(), member); if (current != null) { throw new IllegalArgumentException("Replacing existing member with address: " + member); } current = uuidMap.put(member.getUuid(), member); if (current != null) { throw new IllegalArgumentException("Replacing existing member with uuid: " + member); } } MemberImpl getMember(Address address) { return addressToMemberMap.get(address); } MemberImpl getMember(String uuid) { return uuidToMemberMap.get(uuid); } MemberImpl getMember(Address address, String uuid) { MemberImpl member1 = addressToMemberMap.get(address); MemberImpl member2 = uuidToMemberMap.get(uuid); if (member1 != null && member2 != null && member1.equals(member2)) { return member1; } return null; } boolean contains(Address address) { return addressToMemberMap.containsKey(address); } boolean contains(String uuid) { return uuidToMemberMap.containsKey(uuid); } Set<MemberImpl> getMembers() { return members; } Collection<Address> getAddresses() { return unmodifiableCollection(addressToMemberMap.keySet()); } int size() { return members.size(); } int getVersion() { return version; } MembersView toMembersView() { return MembersView.createNew(version, members); } Set<MemberImpl> tailMemberSet(MemberImpl member, boolean inclusive) { ensureMemberExist(member); Set<MemberImpl> result = new LinkedHashSet<MemberImpl>(); boolean found = false; for (MemberImpl m : members) { if (!found && m.equals(member)) { found = true; if (inclusive) { result.add(m); } continue; } if (found) { result.add(m); } } assert found : member + " should have been found!"; return result; } Set<MemberImpl> headMemberSet(Member member, boolean inclusive) { ensureMemberExist(member); Set<MemberImpl> result = new LinkedHashSet<MemberImpl>(); for (MemberImpl m : members) { if (!m.equals(member)) { result.add(m); continue; } if (inclusive) { result.add(m); } break; } return result; } boolean isBeforeThan(Address address1, Address address2) { if (address1.equals(address2)) { return false; } if (!addressToMemberMap.containsKey(address1)) { return false; } if (!addressToMemberMap.containsKey(address2)) { return false; } for (MemberImpl member : members) { if (member.getAddress().equals(address1)) { return true; } if (member.getAddress().equals(address2)) { return false; } } throw new AssertionError("Unreachable!"); } private void ensureMemberExist(Member member) { if (!addressToMemberMap.containsKey(member.getAddress())) { throw new IllegalArgumentException(member + " not found!"); } if (!uuidToMemberMap.containsKey(member.getUuid())) { throw new IllegalArgumentException(member + " not found!"); } } }