/* * 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.partition.impl; import com.hazelcast.internal.partition.InternalPartition; import com.hazelcast.internal.partition.PartitionListener; import com.hazelcast.nio.Address; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.Arrays; public class InternalPartitionImpl implements InternalPartition { @SuppressFBWarnings(value = "VO_VOLATILE_REFERENCE_TO_ARRAY", justification = "The contents of this array will never be updated, so it can be safely read using a volatile read." + " Writing to `addresses` is done under InternalPartitionServiceImpl.lock," + " so there's no need to guard `addresses` field or to use a CAS.") private volatile Address[] addresses = new Address[MAX_REPLICA_COUNT]; private final int partitionId; private final PartitionListener partitionListener; private final Address thisAddress; private volatile boolean isMigrating; InternalPartitionImpl(int partitionId, PartitionListener partitionListener, Address thisAddress) { this.partitionId = partitionId; this.partitionListener = partitionListener; this.thisAddress = thisAddress; } @SuppressFBWarnings("EI_EXPOSE_REP") public InternalPartitionImpl(int partitionId, PartitionListener listener, Address thisAddress, Address[] addresses) { this(partitionId, listener, thisAddress); this.addresses = addresses; } @Override public int getPartitionId() { return partitionId; } @Override public boolean isMigrating() { return isMigrating; } public void setMigrating(boolean isMigrating) { this.isMigrating = isMigrating; } @Override public boolean isLocal() { return thisAddress.equals(getOwnerOrNull()); } @Override public Address getOwnerOrNull() { return addresses[0]; } @Override public Address getReplicaAddress(int replicaIndex) { return addresses[replicaIndex]; } /** Swaps the addresses for {@code index1} and {@code index2} and call the partition listeners */ void swapAddresses(int index1, int index2) { Address[] newAddresses = Arrays.copyOf(addresses, MAX_REPLICA_COUNT); Address a1 = newAddresses[index1]; Address a2 = newAddresses[index2]; newAddresses[index1] = a2; newAddresses[index2] = a1; addresses = newAddresses; callPartitionListener(index1, a1, a2); callPartitionListener(index2, a2, a1); } // Not doing a defensive copy of given Address[] // This method is called under InternalPartitionServiceImpl.lock, // so there's no need to guard `addresses` field or to use a CAS. void setInitialReplicaAddresses(Address[] newAddresses) { Address[] oldAddresses = addresses; for (int replicaIndex = 0; replicaIndex < MAX_REPLICA_COUNT; replicaIndex++) { if (oldAddresses[replicaIndex] != null) { throw new IllegalStateException("Partition is already initialized!"); } } addresses = newAddresses; } // Not doing a defensive copy of given Address[] // This method is called under InternalPartitionServiceImpl.lock, // so there's no need to guard `addresses` field or to use a CAS. void setReplicaAddresses(Address[] newAddresses) { Address[] oldAddresses = addresses; addresses = newAddresses; callPartitionListener(newAddresses, oldAddresses); } void setReplicaAddress(int replicaIndex, Address newAddress) { Address[] newAddresses = Arrays.copyOf(addresses, MAX_REPLICA_COUNT); Address oldAddress = newAddresses[replicaIndex]; newAddresses[replicaIndex] = newAddress; addresses = newAddresses; callPartitionListener(replicaIndex, oldAddress, newAddress); } /** Calls the partition listener for all changed addresses. */ private void callPartitionListener(Address[] newAddresses, Address[] oldAddresses) { if (partitionListener != null) { for (int replicaIndex = 0; replicaIndex < MAX_REPLICA_COUNT; replicaIndex++) { Address oldAddress = oldAddresses[replicaIndex]; Address newAddress = newAddresses[replicaIndex]; callPartitionListener(replicaIndex, oldAddress, newAddress); } } } /** Sends a {@link PartitionReplicaChangeEvent} if the address has changed. */ private void callPartitionListener(int replicaIndex, Address oldAddress, Address newAddress) { boolean changed; if (oldAddress == null) { changed = newAddress != null; } else { changed = !oldAddress.equals(newAddress); } if (changed) { PartitionReplicaChangeEvent event = new PartitionReplicaChangeEvent(partitionId, replicaIndex, oldAddress, newAddress); partitionListener.replicaChanged(event); } } InternalPartitionImpl copy(PartitionListener listener) { return new InternalPartitionImpl(partitionId, listener, thisAddress, Arrays.copyOf(addresses, MAX_REPLICA_COUNT)); } Address[] getReplicaAddresses() { return addresses; } @Override public boolean isOwnerOrBackup(Address address) { return getReplicaIndex(address) >= 0; } @Override public int getReplicaIndex(Address address) { return getReplicaIndex(addresses, address); } /** * Returns the index of the {@code address} in {@code addresses} or -1 if the {@code address} is {@code null} or * not present. */ public static int getReplicaIndex(Address[] addresses, Address address) { if (address == null) { return -1; } for (int i = 0; i < MAX_REPLICA_COUNT; i++) { if (address.equals(addresses[i])) { return i; } } return -1; } int replaceAddress(Address oldAddress, Address newAddress) { Address[] currentAddresses = addresses; for (int i = 0; i < MAX_REPLICA_COUNT; i++) { Address address = currentAddresses[i]; if (address == null) { break; } if (address.equals(oldAddress)) { Address[] newAddresses = Arrays.copyOf(currentAddresses, MAX_REPLICA_COUNT); newAddresses[i] = newAddress; addresses = newAddresses; callPartitionListener(i, oldAddress, newAddress); return i; } } return -1; } void reset() { addresses = new Address[MAX_REPLICA_COUNT]; setMigrating(false); } @Override public String toString() { StringBuilder sb = new StringBuilder("Partition [").append(partitionId).append("]{\n"); for (int i = 0; i < MAX_REPLICA_COUNT; i++) { Address address = addresses[i]; if (address != null) { sb.append('\t'); sb.append(i).append(":").append(address); sb.append("\n"); } } sb.append("}"); return sb.toString(); } }