org.siyapath.gossip.GossipImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.siyapath.gossip.GossipImpl.java

Source

/*
 * Distributed under the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF 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.siyapath.gossip;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.siyapath.NodeContext;
import org.siyapath.NodeInfo;
import org.siyapath.NodeResource;
import org.siyapath.SiyapathConstants;
import org.siyapath.service.Siyapath;
import org.siyapath.utils.CommonUtils;

import java.net.ConnectException;
import java.sql.Timestamp;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.Set;

/**
 * This class provides overall gossip related operation implementation
 */
public final class GossipImpl {

    private static final Log log = LogFactory.getLog(GossipImpl.class);
    private NodeContext nodeContext;

    /**
     * @param nodeContext
     */
    public GossipImpl(NodeContext nodeContext) {
        this.nodeContext = nodeContext;
    }

    /**
     * Is invoked when a remote node selects this node and gossips the member set.
     * Updates this node's member set by merging the two sets.
     *
     * @param receivedMemberSet member set received from remote node
     * @return the initial member set of this node
     */
    public Set<NodeInfo> memberDiscovery(NodeInfo nodeInfo, Set<NodeInfo> receivedMemberSet) {
        log.info("Remote node invoked member gossip with this node");
        Set<NodeInfo> initialSet = (Set<NodeInfo>) ((HashSet<NodeInfo>) nodeContext.getMemberSet()).clone();
        Set<NodeInfo> newSet = mergeSets(initialSet, receivedMemberSet);
        nodeContext.updateMemberSet(newSet);
        nodeContext.addMemNodeSet(nodeInfo, receivedMemberSet);

        return initialSet;
    }

    /**
     * Is invoked when a remote node requests for
     * a member set from bootstrapper
     *
     * @return the known member set
     */
    public Set<NodeInfo> getMembers() {
        Set<NodeInfo> members = nodeContext.getMemberSet();
        Set<NodeInfo> newSet = new HashSet<NodeInfo>();
        if (members.size() <= SiyapathConstants.MEMBER_SET_LIMIT) {
            return members;
        } else {
            Iterator memIter = members.iterator();
            while (memIter.hasNext() && newSet.size() < SiyapathConstants.MEMBER_SET_LIMIT) {
                NodeInfo member = (NodeInfo) memIter.next();
                newSet.add(member);
            }
            return newSet;
        }
    }

    /**
     * Is invoked when a remote node selects this node and gossips its resource.
     * Updates this node's node resource set
     *
     * @param receivedResourceNodes received from remote node
     * @return the node resource of this node
     */
    public Map<Integer, NodeResource> resourceGossip(Map<Integer, NodeResource> receivedResourceNodes) {
        log.info("Remote node invoked resource gossip with this node");
        Map<Integer, NodeResource> initialSet = (Map<Integer, NodeResource>) ((HashMap<Integer, NodeResource>) nodeContext
                .getMemberResourceMap()).clone();
        Map<Integer, NodeResource> newSet = mergeNewNodeResource(initialSet, receivedResourceNodes);
        nodeContext.updateMemberResourceSet(newSet);
        return nodeContext.getPartialResourceNodes();
    }

    /**
     * Initiates gossiping of member set. Select a random member form the known member
     * list and gossip current member list by calling memberDiscovery service &
     * update the current list with the response
     */
    public void memberGossiper() {

        NodeInfo randomMember = nodeContext.getRandomMember();
        log.info("Getting a random member to gossip:" + randomMember);
        if (randomMember != null) {
            TTransport transport = new TSocket(randomMember.getIp(), randomMember.getPort());
            //TTransport transport = new TFramedTransport(new TSocket("localhost",  randomMember.getPort()));
            try {
                transport.open();
                TProtocol protocol = new TBinaryProtocol(transport);
                Siyapath.Client client = new Siyapath.Client(protocol);
                Set<NodeInfo> nodesToGossip = getDiff(randomMember);
                Set<NodeInfo> discoveredNodes = CommonUtils.deSerialize(client.memberDiscovery(
                        CommonUtils.serialize(nodeContext.getNodeInfo()), CommonUtils.serialize(nodesToGossip)));
                Set<NodeInfo> newSet = mergeSets(nodeContext.getMemberSet(), discoveredNodes);
                nodeContext.updateMemberSet(newSet);
                nodeContext.addMemNodeSet(randomMember, discoveredNodes);
                log.info("Members fetched " + discoveredNodes.size());
            } catch (SecurityException e) {
                log.error("Could not member gossip due to Security Exception");
            } catch (TTransportException e) {
                if (e.getCause() instanceof ConnectException) {
                    log.error("Could not connect to the member node for member gossiping");
                    nodeContext.removeMember(randomMember);
                    nodeContext.removeMemNodeSet(randomMember);
                } else {
                    log.error("Member Gossip failed :" + randomMember.getNodeId() + e.getMessage());
                }
            } catch (TException e) {
                log.error("Member Gossip failed :" + randomMember.getNodeId() + e.getMessage());
            } finally {
                transport.close();
            }
        }
    }

    /**
     * Initiates gossiping of node resource. Select a random member form the known member
     * list and gossip current statistics of node resource list by calling resourcesGossip service &
     * update the current list of node with resource with the response
     */
    public void resourceGossiper() {

        NodeInfo randomMember = nodeContext.getRandomMember();
        log.info("Getting a random member to gossip resources:" + randomMember);
        if (randomMember != null) {
            TTransport transport = new TSocket(randomMember.getIp(), randomMember.getPort());
            //TTransport transport = new TFramedTransport(new TSocket("localhost",  randomMember.getPort()));
            try {
                transport.open();
                TProtocol protocol = new TBinaryProtocol(transport);
                Siyapath.Client client = new Siyapath.Client(protocol);
                Map<Integer, NodeResource> discoveredNodesResource = CommonUtils.deSerialize(
                        client.resourceGossip(CommonUtils.serialize(nodeContext.getPartialResourceNodes())));
                Map<Integer, NodeResource> newSet = mergeNewNodeResource(nodeContext.getMemberResourceMap(),
                        discoveredNodesResource);
                nodeContext.updateMemberResourceSet(newSet);
                log.info("Node Resource Fetched:" + discoveredNodesResource.size());

            } catch (SecurityException e) {
                log.error("Could not resource gossip due to Security Exception");
            } catch (TTransportException e) {
                if (e.getCause() instanceof ConnectException) {
                    log.error("Could not connect to the member node for resource gossiping");
                    nodeContext.removeMember(randomMember);
                    nodeContext.removeFromMemResourceMap(randomMember.getNodeId());
                } else {
                    log.error("Resource Gossip failed with :" + randomMember.getNodeId() + e.getMessage());
                }
            } catch (TException e) {
                log.error("Resource Gossip failed with :" + randomMember.getNodeId() + e.getMessage());

            } finally {
                transport.close();
            }
        }
    }

    /**
     * Merges two Sets keeping the resulting set size under a defined limit
     *
     * @param initialSet
     * @param discoveredSet
     * @return new merged Set of node info
     */
    private Set<NodeInfo> mergeSets(Set initialSet, Set discoveredSet) {
        Set<NodeInfo> newSet = initialSet;
        Set<NodeInfo> tempSet = new HashSet<NodeInfo>();
        for (Iterator iterator = discoveredSet.iterator(); tempSet.size() < 0.5 * SiyapathConstants.MEMBER_SET_LIMIT
                && iterator.hasNext();) {
            NodeInfo member = (NodeInfo) iterator.next();
            if (!initialSet.contains(member) && !member.equals(nodeContext.getNodeInfo())) {
                tempSet.add(member);
            }
        }
        for (int i = tempSet.size(); i > 0 && newSet.size() > 0.5 * SiyapathConstants.MEMBER_SET_LIMIT; i--) {
            newSet.remove(newSet.iterator().next());
        }
        newSet.addAll(tempSet);
        return newSet;
    }

    /**
     * Merge a given set of discoveredResourceNodes with existing nodeResource Map up
     * to a given limit and by comparing timestamps
     *
     * @param initialMap
     * @param discoveredResourceNodes
     * @return new merged Set of node resource
     */
    private Map<Integer, NodeResource> mergeNewNodeResource(Map initialMap,
            Map<Integer, NodeResource> discoveredResourceNodes) {
        Map<Integer, NodeResource> newMap = initialMap;

        //Iterate through the discoveredResourceNodes
        for (Map.Entry<Integer, NodeResource> entry : discoveredResourceNodes.entrySet()) {
            int key = entry.getKey();
            NodeResource value = entry.getValue();
            if (key != nodeContext.getNodeInfo().getNodeId()) {
                //Check for MemberResource Map limit
                if (newMap.size() < SiyapathConstants.RESOURCE_MEMBER_SET_LIMIT) {
                    if (initialMap.containsKey(key)) {
                        //Compare timestamps
                        NodeResource initialResource = (NodeResource) initialMap.get(key);
                        Timestamp initialStamp = new Timestamp(initialResource.getTimeStamp());
                        Timestamp newStamp = new Timestamp(value.getTimeStamp());
                        if (initialStamp.before(newStamp)) {
                            newMap.put(key, value);
                        }
                    } else {
                        newMap.put(key, value);
                    }

                } else {
                    //select a random node to remove
                    int newKey = newMap.entrySet().iterator().next().getKey();
                    if (initialMap.containsKey(key)) {
                        //Compare timestamps
                        NodeResource initialResource = (NodeResource) initialMap.get(key);
                        Timestamp initialStamp = new Timestamp(initialResource.getTimeStamp());
                        Timestamp newStamp = new Timestamp(value.getTimeStamp());
                        if (initialStamp.before(newStamp)) {
                            newMap.put(key, value);
                        }
                    } else {
                        newMap.remove(newKey);
                        newMap.put(key, value);

                    }
                }
            }
        }

        return newMap;
    }

    /**
     * Selects a Diff of nodeSet to Gossip by comparing past nodeSet with current nodeSet
     *
     * @param gossipMember
     * @return diff of nodeSet
     */
    private Set<NodeInfo> getDiff(NodeInfo gossipMember) {
        HashSet<NodeInfo> tempSet = new HashSet<NodeInfo>();
        HashSet<NodeInfo> initialSet = (HashSet<NodeInfo>) ((HashSet<NodeInfo>) nodeContext.getMemberSet()).clone();
        initialSet.add(nodeContext.getNodeInfo());
        HashSet<NodeInfo> memberSet = (HashSet<NodeInfo>) nodeContext.getMemNodeSet(gossipMember);
        if (memberSet != null) {
            for (NodeInfo newNode : initialSet) {
                if (!memberSet.contains(newNode)) {
                    tempSet.add(newNode);
                }
            }
            return tempSet;
        }
        return initialSet;
    }
}