org.wso2.andes.server.cluster.ClusterManager.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.andes.server.cluster.ClusterManager.java

Source

/*
 * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you 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 org.wso2.andes.server.cluster;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.andes.configuration.AndesConfigurationManager;
import org.wso2.andes.configuration.enums.AndesConfiguration;
import org.wso2.andes.kernel.Andes;
import org.wso2.andes.kernel.AndesContext;
import org.wso2.andes.kernel.AndesContextStore;
import org.wso2.andes.kernel.AndesException;
import org.wso2.andes.kernel.slot.SlotManagerClusterMode;
import org.wso2.andes.kernel.slot.SlotMessageCounter;
import org.wso2.andes.server.ClusterResourceHolder;
import org.wso2.andes.server.cluster.coordination.CoordinationConstants;
import org.wso2.andes.store.FailureObservingStoreManager;
import org.wso2.andes.store.HealthAwareStore;
import org.wso2.andes.store.StoreHealthListener;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * Cluster Manager is responsible for Handling the Broker Cluster Management Tasks like
 * Queue Worker distribution. Fail over handling for cluster nodes. etc.
 */
public class ClusterManager implements StoreHealthListener {

    /**
     * Class logger
     */
    private Log log = LogFactory.getLog(ClusterManager.class);

    /**
     * Id of the local node
     */
    private String nodeId;

    /**
     * AndesContextStore instance
     */
    private AndesContextStore andesContextStore;

    /**
     * Cluster agent for managing cluster communication
     */
    private ClusterAgent clusterAgent;

    /**
     * This is the cached value of the operational status of the store
     */
    private boolean storeOperational;

    private ScheduledExecutorService slotRecoveryTaskService;

    public static final int SLOT_SUBMIT_TASK_POOL_SIZE = 1;

    /**
     * Create a ClusterManager instance
     */
    public ClusterManager() {
        this.andesContextStore = AndesContext.getInstance().getAndesContextStore();
    }

    /**
     * Initialize the Cluster manager.
     *
     * @throws AndesException
     */
    public void init() throws AndesException {

        if (AndesContext.getInstance().isClusteringEnabled()) {
            initClusterMode();
            ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                    .setNameFormat("SlotRecoveryOneTimeTask-%d").build();
            slotRecoveryTaskService = Executors.newScheduledThreadPool(SLOT_SUBMIT_TASK_POOL_SIZE,
                    namedThreadFactory);
        } else {
            initStandaloneMode();
        }
        // set storeOperational to true since it can be assumed that the store is operational at startup
        // if it is non-operational, the value will be updated immediately
        storeOperational = true;
        // Register this instance to listen to the store health
        FailureObservingStoreManager.registerStoreHealthListener(this);

    }

    /**
     * Handles changes needs to be done in current node when a node joins to the cluster.
     *
     * @param addedNodeId An ID for the newly added node. This is does not refer to the correct node ID. I.E its not a
     *                    reference to the member's node ID attribute.
     */
    public void memberAdded(String addedNodeId) {
        log.info("Handling cluster gossip: Node " + addedNodeId + "  Joined the Cluster");
    }

    /**
     * Handles changes needs to be done in current node when a node leaves the cluster
     * @param deletedNodeId deleted node id
     */
    public void memberRemoved(String deletedNodeId) throws AndesException {
        log.info("Handling cluster gossip: Node " + deletedNodeId + "  left the Cluster");

        if (clusterAgent.isCoordinator()) {
            SlotManagerClusterMode.getInstance().deletePublisherNode(deletedNodeId);

            //Reassign the slot to free slots pool
            SlotManagerClusterMode.getInstance().reassignSlotsWhenMemberLeaves(deletedNodeId);

            // Schedule a slot recovery task for lost submit slot event from left member node
            log.info("Scheduling slot recovery task.");
            slotRecoveryTaskService.schedule(new Runnable() {
                @Override
                public void run() {
                    Andes.getInstance().triggerRecoveryEvent();
                }
            }, SlotMessageCounter.getInstance().SLOT_SUBMIT_TIMEOUT, TimeUnit.MILLISECONDS);

            ClusterResourceHolder.getInstance().getSubscriptionManager()
                    .removeAllSubscriptionsOfNodeFromMemoryAndStore(deletedNodeId);
        } else {
            ClusterResourceHolder.getInstance().getSubscriptionManager()
                    .removeAllSubscriptionsOfNodeFromMemory(deletedNodeId);
        }
    }

    /**
     * Get whether clustering is enabled
     *
     * @return true if clustering is enabled, false otherwise.
     */
    public boolean isClusteringEnabled() {
        return AndesContext.getInstance().isClusteringEnabled();
    }

    /**
     * Get the node ID of the current node
     *
     * @return current node's ID
     */
    public String getMyNodeID() {
        return nodeId;
    }

    /**
     * Perform cleanup tasks before shutdown
     */
    public void prepareLocalNodeForShutDown() throws AndesException {
        //clear stored node IDS and mark subscriptions of node as closed
        clearAllPersistedStatesOfLocalNode();
    }

    /**
     * Gets the unique ID for the local node
     *
     * @return unique ID
     */
    public int getUniqueIdForLocalNode() {
        if (AndesContext.getInstance().isClusteringEnabled()) {
            return clusterAgent.getUniqueIdForLocalNode();
        }
        return 0;
    }

    /**
     * Initialize the node in stand alone mode without hazelcast.
     *
     * @throws AndesException, UnknownHostException
     */
    private void initStandaloneMode() throws AndesException {

        try {
            // Get Node ID configured by user in broker.xml (if not "default" we must use it as the ID)
            this.nodeId = AndesConfigurationManager.readValue(AndesConfiguration.COORDINATION_NODE_ID);

            if (AndesConfiguration.COORDINATION_NODE_ID.get().getDefaultValue().equals(this.nodeId)) {
                this.nodeId = CoordinationConstants.NODE_NAME_PREFIX + InetAddress.getLocalHost().toString();
            }

            //update node information in durable store
            List<String> nodeList = new ArrayList<>(andesContextStore.getAllStoredNodeData().keySet());

            for (String node : nodeList) {
                andesContextStore.removeNodeData(node);
            }

            log.info("Initializing Standalone Mode. Current Node ID:" + this.nodeId + " "
                    + InetAddress.getLocalHost().getHostAddress());

            andesContextStore.storeNodeDetails(nodeId, InetAddress.getLocalHost().getHostAddress());
        } catch (UnknownHostException e) {
            throw new AndesException("Unable to get the localhost address.", e);
        }
    }

    /**
     * Initializes cluster mode
     *
     * @throws AndesException
     */
    private void initClusterMode() throws AndesException {

        // Set the cluster agent from the Andes Context.
        this.clusterAgent = AndesContext.getInstance().getClusterAgent();

        clusterAgent.start(this);

        this.nodeId = clusterAgent.getLocalNodeIdentifier();
        log.info("Initializing Cluster Mode. Current Node ID:" + this.nodeId);

        String localMemberHostAddress = clusterAgent.getLocalNodeIdentifier();

        if (log.isDebugEnabled()) {
            log.debug("Stored node ID : " + this.nodeId + ". Stored node data(Hazelcast local "
                    + "member host address) : " + localMemberHostAddress);
        }
    }

    /**
     * Clears all persisted states of a disappeared node
     *
     * @param nodeID node ID
     * @throws AndesException
     */
    private void clearAllPersistedStatesOfDisappearedNode(String nodeID) throws AndesException {

        log.info("Clearing the Persisted State of Node with ID " + nodeID);
        andesContextStore.removeNodeData(nodeID);

        ClusterResourceHolder.getInstance().getSubscriptionManager().removeAllSubscriptionsOfNodeFromMemory(nodeID);

    }

    /**
     * Clears all persisted states of the local node.
     *
     * @throws AndesException if an error is occured when closing the connection or removing the subscription.
     */
    private void clearAllPersistedStatesOfLocalNode() throws AndesException {

        log.info("Clearing the Persisted State of Node with ID " + this.nodeId);
        andesContextStore.removeNodeData(nodeId);
        ClusterResourceHolder.getInstance().getSubscriptionManager().closeAllActiveLocalSubscriptions();
    }

    /**
     * Perform coordinator initialization tasks, when this node is elected as the new coordinator
     */
    public void localNodeElectedAsCoordinator() {
    }

    /**
     * Gets details  of all the members in the cluster in the format of,
     *    "node_ID,host_address,port,isCoordinator->(true|false)"
     *
     * @return A list of address of the nodes in a cluster
     */
    public List<String> getAllClusterNodeAddresses() throws AndesException {

        if (AndesContext.getInstance().isClusteringEnabled()) {
            return clusterAgent.getAllClusterNodeAddresses();
        }
        ArrayList<String> nodes = new ArrayList<>();
        nodes.add(getMyNodeID());
        return nodes;
    }

    /**
     * Gets the message store's health status
     *
     * @return true if healthy, else false.
     */
    public boolean getStoreHealth() {
        return storeOperational;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void storeNonOperational(HealthAwareStore store, Exception ex) {
        log.warn("Store became non-operational.");
        storeOperational = false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void storeOperational(HealthAwareStore store) {
        storeOperational = true;
        log.info("Store became operational.");
    }
}