de.tud.kom.p2psim.impl.overlay.dht.kademlia2.setup.AbstractNodeFactory.java Source code

Java tutorial

Introduction

Here is the source code for de.tud.kom.p2psim.impl.overlay.dht.kademlia2.setup.AbstractNodeFactory.java

Source

/*
 * Copyright (c) 2005-2011 KOM - Multimedia Communications Lab
 *
 * This file is part of PeerfactSim.KOM.
 * 
 * PeerfactSim.KOM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * PeerfactSim.KOM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PeerfactSim.KOM.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package de.tud.kom.p2psim.impl.overlay.dht.kademlia2.setup;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.apache.commons.math.random.RandomAdaptor;
import org.apache.log4j.Logger;

import de.tud.kom.p2psim.api.common.Component;
import de.tud.kom.p2psim.api.common.ComponentFactory;
import de.tud.kom.p2psim.api.common.Host;
import de.tud.kom.p2psim.impl.overlay.AbstractOverlayNode.PeerStatus;
import de.tud.kom.p2psim.impl.overlay.dht.kademlia2.components.Node;
import de.tud.kom.p2psim.impl.overlay.dht.kademlia2.types.HKademliaOverlayID;
import de.tud.kom.p2psim.impl.overlay.dht.kademlia2.types.KademliaOverlayContact;
import de.tud.kom.p2psim.impl.overlay.dht.kademlia2.types.KademliaOverlayID;
import de.tud.kom.p2psim.impl.overlay.dht.kademlia2.types.KademliaOverlayKey;
import de.tud.kom.p2psim.impl.simengine.Simulator;
import de.tud.kom.p2psim.impl.util.logging.SimLogger;
import de.tud.kom.p2psim.impl.util.toolkits.KSmallestMap;
import de.tud.kom.p2psim.impl.util.toolkits.KSortedLookupList;
import de.tud.kom.p2psim.impl.util.toolkits.Predicate;
import de.tud.kom.p2psim.impl.util.toolkits.Comparators.KademliaOverlayIDXORMaxComparator;

/**
 * Superclass for Kademlia node factories. Subclasses have to implement
 * construction of concrete Nodes.
 * 
 * @author Tobias Lauinger <tl1003@rbg.informatik.tu-darmstadt.de>
 * @version 05/06/2011
 */
public abstract class AbstractNodeFactory implements ComponentFactory {

    private final static Logger log = SimLogger.getLogger(AbstractNodeFactory.class);

    /**
     * The local port on which Kademlia nodes listen for incoming messages.
     */
    private static final short LOCAL_KADEMLIA_PORT = 1605;

    /**
     * A Predicate that holds for all Nodes that are not ABSENT.
     */
    private static final Predicate<Node<?>> notAbsent = new Predicate<Node<?>>() {

        @Override
        public final boolean isTrue(final Node<?> peer) {
            return peer.getPeerStatus() != PeerStatus.ABSENT;
        }

    };

    /**
     * The random generator that is used in this simulation - a copy of
     * {@link Simulator#getRandom()}.
     */
    protected final Random rnd;

    /**
     * Configuration values ("constants").
     */
    protected final Config config;

    /**
     * Map of all Nodes that have been created so far (mapped by their ID).
     */
    private final Map<HKademliaOverlayID, Node<HKademliaOverlayID>> constructedNodes;

    /**
     * A List that contains the constructed Nodes for efficient random access.
     */
    private final List<Node<HKademliaOverlayID>> constructedNodeValues;

    /**
     * Constructs a new AbstractNodeFactory, the necessary constants are read
     * from {@link KademliaSetup}.
     */
    public AbstractNodeFactory() {
        config = KademliaSetup.getConfig();
        this.rnd = new RandomAdaptor(Simulator.getRandom());

        final int approxSize = (int) Math.ceil(config.getNumberOfPeers() * 1.02);
        constructedNodes = new HashMap<HKademliaOverlayID, Node<HKademliaOverlayID>>(approxSize, 0.99f);
        constructedNodeValues = new ArrayList<Node<HKademliaOverlayID>>(approxSize);
    }

    /*
     * Methods to construct Nodes
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public final Component createComponent(final Host host) {
        final Node<HKademliaOverlayID> newNode;
        HKademliaOverlayID id;

        // make sure that no two peers use the same ID (although quite unlikely)
        do {
            id = getRandomHKademliaOverlayID(host);
        } while (constructedNodes.containsKey(id));
        newNode = buildNode(id, LOCAL_KADEMLIA_PORT, host);
        constructedNodes.put(newNode.getTypedOverlayID(), newNode);
        constructedNodeValues.add(newNode);

        log.debug("Built node " + newNode);

        return newNode;
    }

    /**
     * Constructs a new random ID.
     * 
     * @param host
     *            the Host on which the Node will run - can be used to retrieve
     *            scenario information.
     * @return a new random HKademliaOverlayID.
     */
    @SuppressWarnings("unused")
    protected HKademliaOverlayID getRandomHKademliaOverlayID(final Host host) {
        final int bits = config.getIDLength();
        final BigInteger id = new BigInteger(bits, rnd);
        return new HKademliaOverlayID(id, config);
    }

    /**
     * Constructs a new Node with the given initialisation data.
     * 
     * @param id
     *            the HKademliaOverlayID of the new Node.
     * @param port
     *            the port on which the new Node will listen for incoming
     *            messages.
     * @param host
     *            the Host on which the new Node will run.
     * @return a new Node with the given HKademliaOverlayID, port, and transport
     *         layer.
     */
    protected abstract Node<HKademliaOverlayID> buildNode(HKademliaOverlayID id, short port, Host host);

    /*
     * Methods to store initial routing table contents
     */

    /**
     * "Callback" to inform this factory that all nodes have been constructed.
     * Further steps that rely on that fact will be executed: all Nodes get
     * their initial routing table contents.
     * <p>
     * <i>This method should be called only once and only if all Nodes have been
     * constructed!</i>
     * 
     * @param unused
     *            dummy parameter to enable a use of the method in the config
     *            file
     */
    public final void setTriggerInitialRTBuild(String unused) {
        storeInitialRoutingTableContents();
    }

    private void storeInitialRoutingTableContents() {
        log.info("Storing initial routing table contents at nodes...");

        Collection<KademliaOverlayContact<HKademliaOverlayID>> initRTContents;
        for (final Node<HKademliaOverlayID> currentNode : constructedNodeValues) {
            initRTContents = getRandomInitialRoutingTableContents(currentNode);
            currentNode.addContactsToRoutingTable(initRTContents);
        }

        log.info("Initial routing table contents has been set at all nodes.");
    }

    /**
     * Determines the initial routing table contents for Node
     * <code>target</code>.
     * 
     * @param target
     *            the Node that will receive the routing table contents.
     * @return a Collection that contains random KademliaOverlayContacts to be
     *         used as initial routing table contents of a Node.
     */
    /*
     * This base implementation determines the routing table contents randomly &
     * independent of the target Node.
     */
    @SuppressWarnings("unused")
    protected Collection<KademliaOverlayContact<HKademliaOverlayID>> getRandomInitialRoutingTableContents(
            final Node<HKademliaOverlayID> target) {
        return getRandomInitialRoutingTableContentsFromList(constructedNodeValues);
    }

    /**
     * Determines the initial routing table contents for a Node according to the
     * List of candidate entries <code>candidates</code>.
     * 
     * @param candidates
     *            a List from which the routing table contents will be chosen at
     *            random.
     * @return a Collection that contains random KademliaOverlayContacts to be
     *         used as initial routing table contents of a Node.
     */
    protected final Collection<KademliaOverlayContact<HKademliaOverlayID>> getRandomInitialRoutingTableContentsFromList(
            final List<Node<HKademliaOverlayID>> candidates) {
        final int numOfContacts = Math.min(config.getNumberOfInitialRoutingTableContacts(), candidates.size());
        final Set<KademliaOverlayContact<HKademliaOverlayID>> result = new HashSet<KademliaOverlayContact<HKademliaOverlayID>>(
                (int) Math.ceil(numOfContacts * 1.2), 0.9f);
        Node<HKademliaOverlayID> randomNode;

        while (result.size() < numOfContacts) {
            randomNode = candidates.get(rnd.nextInt(candidates.size()));
            result.add(randomNode.getLocalContact());
        }

        return result;
    }

    /*
     * Methods for evaluation/measurement purposes
     */

    /**
     * Determines whether the given Node is currently ABSENT.
     * 
     * @param peer
     *            the KademliaOverlayID of the Node.
     * @return whether the Node is ABSENT.
     */
    public final boolean isOffline(final KademliaOverlayID peer) {
        return constructedNodes.get(peer).getPeerStatus() == PeerStatus.ABSENT;
    }

    /**
     * Determines whether the given peer has a data item with the given key in
     * its local database.
     * 
     * @param peer
     *            the KademliaOverlayID of the peer.
     * @param key
     *            the KademliaOverlayKey of the data item.
     * @return true if the peer has the data item.
     */
    public final boolean hasDataItem(final KademliaOverlayID peer, final KademliaOverlayKey key) {
        return constructedNodes.get(peer).getLocalIndex().get(key) != null;
    }

    /**
     * Determines the {@link KademliaConfig#K} Node-IDs that are closest to
     * <code>key</code> and are online/going online (not PeerStatus.ABSENT) at
     * the point in time of invocation of this method.
     * 
     * @param key
     *            the KademliaOverlayKey which is to be looked up.
     * @return a Collection with the K HKademliaOverlayIDs of nodes that are
     *         closest to <code>key</code>. These are guaranteed to be
     *         online/going online.
     */
    public final Set<HKademliaOverlayID> getKClosestOnlineIDs(final KademliaOverlayKey key) {
        final Comparator<HKademliaOverlayID> xorToKey;
        final KSortedLookupList<HKademliaOverlayID, Node<HKademliaOverlayID>> kClosestFilteredNodes;

        xorToKey = new KademliaOverlayIDXORMaxComparator<HKademliaOverlayID>(key.getBigInt());
        kClosestFilteredNodes = new KSmallestMap<HKademliaOverlayID, Node<HKademliaOverlayID>>(
                config.getBucketSize(), xorToKey);
        kClosestFilteredNodes.putAll(constructedNodes, null, notAbsent);
        return kClosestFilteredNodes.keySet();
    }

    /**
     * Returns all IDs that are closer to key than ref.
     * 
     * @param ref
     *            a reference HKademliaOverlayID.
     * @param key
     *            the KademliaOverlayKey according to which the distance is
     *            calculated.
     * @return a Collection with all HKademliaOverlayIDs of peers that are
     *         closer to key than ref.
     */
    public final Collection<HKademliaOverlayID> getCloserIDs(final HKademliaOverlayID ref,
            final KademliaOverlayKey key) {
        final List<HKademliaOverlayID> closer;
        final Comparator<HKademliaOverlayID> xorToKey;

        closer = new ArrayList<HKademliaOverlayID>(config.getBucketSize() * 2);
        xorToKey = new KademliaOverlayIDXORMaxComparator<HKademliaOverlayID>(key.getBigInt());

        for (final HKademliaOverlayID candidate : constructedNodes.keySet()) {
            if (xorToKey.compare(candidate, ref) < 0) {
                closer.add(candidate);
            }
        }

        return closer;
    }

    /**
     * Determines how many peers have the data item associated with key.
     * 
     * @param key
     *            the KademliaOverlayKey of the data item.
     * @return an array with the total number of peers that have the data as
     *         first entry and the number of online peers that have it as second
     *         entry.
     */
    public final int[] numberOfPeersWithData(final KademliaOverlayKey key) {
        int peersWithData = 0, onlinePeersWithData = 0;

        for (final Node<HKademliaOverlayID> peer : constructedNodeValues) {
            if (peer.getLocalIndex().get(key) != null) {
                peersWithData++;
                if (peer.getPeerStatus() != PeerStatus.ABSENT) {
                    onlinePeersWithData++;
                }
            }
        }

        return new int[] { peersWithData, onlinePeersWithData };
    }

    /*
     * Method for WorkloadGenerator
     */

    /**
     * Determines the {@link KademliaConfig#K} Nodes that are closest to
     * <code>key</code>.
     * 
     * @param key
     *            the KademliaOverlayKey which is to be looked up.
     * @return a Collection with the K Nodes that are closest to
     *         <code>key</code>. These need not all be online.
     */
    /*
     * Note that the underlying KSmallestMap in getKClosestFilteredNodes can be
     * garbage collected only when the result returned here is no longer in use!
     */
    protected final Collection<Node<HKademliaOverlayID>> getKClosestNodes(final KademliaOverlayKey key) {
        final Comparator<HKademliaOverlayID> xorToKey;
        final KSortedLookupList<HKademliaOverlayID, Node<HKademliaOverlayID>> kClosestFilteredNodes;

        xorToKey = new KademliaOverlayIDXORMaxComparator<HKademliaOverlayID>(key.getBigInt());
        kClosestFilteredNodes = new KSmallestMap<HKademliaOverlayID, Node<HKademliaOverlayID>>(
                config.getBucketSize(), xorToKey);
        kClosestFilteredNodes.putAll(constructedNodes);
        return kClosestFilteredNodes.values();
    }

}