All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.internal.partition.impl.InternalPartitionImpl Maven / Gradle / Ivy

/*
 * Copyright (c) 2008-2018, 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();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy