Package net.tomp2p.peers

Source Code of net.tomp2p.peers.PeerMap

/*
* Copyright 2013 Thomas Bocek
*
* 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 net.tomp2p.peers;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.SortedSet;
import java.util.TreeSet;

import net.tomp2p.connection.PeerConnection;
import net.tomp2p.connection.PeerException;
import net.tomp2p.connection.PeerException.AbortCause;
import net.tomp2p.utils.CacheMap;
import net.tomp2p.utils.ConcurrentCacheMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This routing implementation uses is based on Kademlia. However, many changes have been applied to make it faster and
* more flexible. This class is partially thread-safe.
*
* @author Thomas Bocek
*/
public class PeerMap implements PeerStatusListener, Maintainable {
    private static final Logger LOG = LoggerFactory.getLogger(PeerMap.class);

    // each distance bit has its own bag this is the size of the verified peers (the ones that we know are reachable)
    private final int bagSizeVerified;
    private final int bagSizeOverflow;

    // the id of this node
    private final Number160 self;

    // the storage for the peers that are verified
    private final List<Map<Number160, PeerStatistic>> peerMapVerified;

    // the storage for the peers that are not verified or overflown
    private final List<Map<Number160, PeerStatistic>> peerMapOverflow;

    private final ConcurrentCacheMap<Number160, PeerAddress> offlineMap;
    private final ConcurrentCacheMap<Number160, PeerAddress> shutdownMap;
    private final ConcurrentCacheMap<Number160, PeerAddress> exceptionMap;

    // stores listeners that will be notified if a peer gets removed or added
    private final List<PeerMapChangeListener> peerMapChangeListeners = new ArrayList<PeerMapChangeListener>();

    private final Collection<PeerFilter> peerFilters;

    // the number of failures until a peer is considered offline
    private final int offlineCount;

    private final Maintenance maintenance;
   
    private final boolean peerVerification;

    /**
     * Creates the bag for the peers. This peer knows a lot about close peers and the further away the peers are, the
     * less known they are. Distance is measured with XOR of the peer ID. The distance of peer with ID 0x12 and peer
     * with Id 0x28 is 0x3a.
     *
     * @param peerMapConfiguration
     *            The configuration values of this map
     *
     */
  public PeerMap(final PeerMapConfiguration peerMapConfiguration) {
        this.self = peerMapConfiguration.self();
        if (self == null || self.isZero()) {
            throw new IllegalArgumentException("Zero or null are not a valid IDs");
        }
        this.bagSizeVerified = peerMapConfiguration.bagSizeVerified();
        this.bagSizeOverflow = peerMapConfiguration.bagSizeOverflow();
        this.offlineCount = peerMapConfiguration.offlineCount();
        this.peerFilters = peerMapConfiguration.peerFilters();
        this.peerMapVerified = initFixedMap(bagSizeVerified, false);
        this.peerMapOverflow = initFixedMap(bagSizeOverflow, true);
        // bagSizeVerified * Number160.BITS should be enough
        this.offlineMap = new ConcurrentCacheMap<Number160, PeerAddress>(
                peerMapConfiguration.offlineTimeout(), bagSizeVerified * Number160.BITS);
        this.shutdownMap = new ConcurrentCacheMap<Number160, PeerAddress>(
                peerMapConfiguration.shutdownTimeout(), bagSizeVerified * Number160.BITS);
        this.exceptionMap = new ConcurrentCacheMap<Number160, PeerAddress>(
                peerMapConfiguration.exceptionTimeout(), bagSizeVerified * Number160.BITS);
        this.maintenance = peerMapConfiguration.maintenance().init(peerMapVerified, peerMapOverflow,
                offlineMap, shutdownMap, exceptionMap);
        this.peerVerification = peerMapConfiguration.isPeerVerification();
    }

    /**
     * Create a fixed size bag with an unmodifiable map.
     *
     * @param bagSize
     *            The bag size
     * @param caching
     *            If a caching map should be created
     * @return The list of bags containing an unmodifiable map
     */
    private List<Map<Number160, PeerStatistic>> initFixedMap(final int bagSize, final boolean caching) {
        List<Map<Number160, PeerStatistic>> tmp = new ArrayList<Map<Number160, PeerStatistic>>();
        for (int i = 0; i < Number160.BITS; i++) {
            // I made some experiments here and concurrent sets are not
            // necessary, as we divide similar to segments aNonBlockingHashSets
            // in a concurrent map. In a full network, we have 160 segments, for
            // smaller we see around 3-4 segments, growing with the number of
            // peers. bags closer to 0 will see more read than write, and bags
            // closer to 160 will see more writes than reads.
            //
            // We also only allocate memory for the bags far away, as they are likely to be filled first.
            if (caching) {
                tmp.add(new CacheMap<Number160, PeerStatistic>(bagSize, true));
            } else {
                final int memAlloc = Math.max(0, bagSize - (Number160.BITS - i));
                tmp.add(new HashMap<Number160, PeerStatistic>(memAlloc));
            }
        }
        return Collections.unmodifiableList(tmp);
    }

    /**
     * Add a map change listener. This is thread-safe
     *
     * @param peerMapChangeListener
     *            The listener
     */
    public void addPeerMapChangeListener(final PeerMapChangeListener peerMapChangeListener) {
        synchronized (peerMapChangeListeners) {
            peerMapChangeListeners.add(peerMapChangeListener);
        }

    }

    /**
     * Remove a map change listener. This is thread-safe
     *
     * @param peerMapChangeListener
     *            The listener
     */
    public void removePeerMapChangeListener(final PeerMapChangeListener peerMapChangeListener) {
        synchronized (peerMapChangeListeners) {
            peerMapChangeListeners.add(peerMapChangeListener);
        }
    }

    /**
     * Notifies on insert. This is called after the peer has been added to the map.
     *
     * @param peerAddress
     *            The address of the inserted peers
     * @param verified
     *            True if the peer was inserted in the verified map
     */
    private void notifyInsert(final PeerAddress peerAddress, final boolean verified) {
        synchronized (peerMapChangeListeners) {
            for (PeerMapChangeListener listener : peerMapChangeListeners) {
                listener.peerInserted(peerAddress, verified);
            }
        }
    }

    /**
     * Notifies on remove. Since listeners are never changed, this is thread safe.
     *
     * @param peerAddress
     *            The address of the removed peers
     * @param storedPeerAddress
     *            Contains information statistical information
     */
    private void notifyRemove(final PeerAddress peerAddress, final PeerStatistic storedPeerAddress) {
        synchronized (peerMapChangeListeners) {
            for (PeerMapChangeListener listener : peerMapChangeListeners) {
                listener.peerRemoved(peerAddress, storedPeerAddress);
            }
        }
    }

    /**
     * Notifies on update. This method is thread safe.
     *
     * @param peerAddress
     *            The address of the updated peers.
     * @param storedPeerAddress
     *            Contains information statistical information
     */
    private void notifyUpdate(final PeerAddress peerAddress, final PeerStatistic storedPeerAddress) {
        synchronized (peerMapChangeListeners) {
            for (PeerMapChangeListener listener : peerMapChangeListeners) {
                listener.peerUpdated(peerAddress, storedPeerAddress);
            }
        }
    }

    /**
     * The number of the peers in the verified map.
     *
     * @return the total number of peers
     */
    public int size() {
        int size = 0;
        for (Map<Number160, PeerStatistic> map : peerMapVerified) {
            synchronized (map) {
                size += map.size();
            }
        }
        return size;
    }

    /**
     * Each node that has a bag has an ID itself to define what is close. This method returns this ID.
     *
     * @return The id of this node
     */
    public Number160 self() {
        return self;
    }
   
    private boolean reject (PeerAddress peerAddress) {
      if(peerFilters == null || peerFilters.size() ==0) {
        return false;
      }
      final Collection<PeerAddress> all = all();
      for(PeerFilter peerFilter:peerFilters) {
        if(peerFilter.reject(peerAddress, all, self)) {
          return true;
        }
      }
      return false;
    }
   

    /**
     * Adds a neighbor to the neighbor list. If the bag is full, the id zero or the same as our id, the neighbor is not
     * added. This method is tread-safe
     *
     * @param remotePeer
     *            The node that should be added
     * @param referrer
     *            If we had direct contact and we know for sure that this node is online, we set firsthand to true.
     *            Information from 3rd party peers are always second hand and treated as such
     * @return True if the neighbor could be added or updated, otherwise false.
     */
    @Override
    public boolean peerFound(final PeerAddress remotePeer, final PeerAddress referrer, final PeerConnection peerConnection) {     
      LOG.debug("peer {} is online reporter was {}", remotePeer, referrer);
        boolean firstHand = referrer == null;
        //if we got contacted by this peer, but we did not initiate the connection
        boolean secondHand = remotePeer.equals(referrer);
        //if a peer reported about other peers
        boolean thirdHand = !firstHand && !secondHand;
        // always trust first hand information
        if (firstHand) {
            offlineMap.remove(remotePeer.peerId());
            shutdownMap.remove(remotePeer.peerId());
        }
       
        if (secondHand && !peerVerification) {
          offlineMap.remove(remotePeer.peerId());
            shutdownMap.remove(remotePeer.peerId());
        }
       
        // don't add nodes with zero node id, do not add myself and do not add
        // nodes marked as bad
        if (remotePeer.peerId().isZero() || self().equals(remotePeer.peerId()) || reject(remotePeer)) {
            return false;
        }
       
        if (remotePeer.isFirewalledTCP() || remotePeer.isFirewalledUDP()) {
          return false;
        }
       
        //if a peer is relayed but cannot provide any relays, its useless
        if (remotePeer.isRelayed() && remotePeer.peerSocketAddresses().isEmpty()) {
          return false;
        }
       
        final boolean probablyDead = offlineMap.containsKey(remotePeer.peerId()) ||
            shutdownMap.containsKey(remotePeer.peerId()) ||
            exceptionMap.containsKey(remotePeer.peerId());
       
        if(thirdHand && probablyDead) {
          LOG.debug("don't add {}", remotePeer.peerId());
          return false;
        }
       
        final int classMember = classMember(remotePeer.peerId());

        // the peer might have a new port
        final PeerStatistic oldPeerStatatistic = updateExistingVerifiedPeerAddress(
                peerMapVerified.get(classMember), remotePeer, firstHand);
        if (oldPeerStatatistic != null) {
            // we update the peer, so we can exit here and report that we have
            // updated it.
            notifyUpdate(remotePeer, oldPeerStatatistic);
            return true;
        } else {
            if (firstHand || (secondHand && !peerVerification)) {
                final Map<Number160, PeerStatistic> map = peerMapVerified.get(classMember);
                boolean insterted = false;
                synchronized (map) {
                    // check again, now we are synchronized
                    if (map.containsKey(remotePeer.peerId())) {
                        return peerFound(remotePeer, referrer, peerConnection);
                    }
                    if (map.size() < bagSizeVerified) {
                        final PeerStatistic peerStatatistic = new PeerStatistic(remotePeer);
                        peerStatatistic.successfullyChecked();
                        map.put(remotePeer.peerId(), peerStatatistic);
                        insterted = true;
                    }
                }

                if (insterted) {
                    // if we inserted into the verified map, remove it from the non-verified map
                    final Map<Number160, PeerStatistic> mapOverflow = peerMapOverflow.get(classMember);
                    synchronized (mapOverflow) {
                        mapOverflow.remove(remotePeer.peerId());
                    }
                    notifyInsert(remotePeer, true);
                    return true;
                }
            }
        }
        // if we are here, we did not have this peer, but our verified map was full
        // check if we have it stored in the non verified map.
        final Map<Number160, PeerStatistic> mapOverflow = peerMapOverflow.get(classMember);
        synchronized (mapOverflow) {
            PeerStatistic peerStatatistic = mapOverflow.get(remotePeer.peerId());
            if (peerStatatistic == null) {
                peerStatatistic = new PeerStatistic(remotePeer);
            }
            if (firstHand) {
                peerStatatistic.successfullyChecked();
            }
            mapOverflow.put(remotePeer.peerId(), peerStatatistic);
        }

        notifyInsert(remotePeer, false);
        return true;
    }

    /**
     * Remove a peer from the list. In order to not reappear, the node is put for a certain time in a cache list to keep
     * the node removed. This method is thread-safe.
     *
     * @param remotePeer
     *            The node that should be removed
     * @param force
     *            A flag that removes a peer immediately.
     * @return True if the neighbor was removed and added to a cache list. False if peer has not been removed or is
     *         already in the peer removed temporarily list.
     */
    @Override
    public boolean peerFailed(final PeerAddress remotePeer, final PeerException peerException) {
        LOG.debug("peer {} is offline with reason {}", remotePeer, peerException);
       
        // TB: ignore ZERO peer Id for the moment, but we should filter for the IP address
        if (remotePeer.peerId().isZero() || self().equals(remotePeer.peerId())) {
            return false;
        }
        final int classMember = classMember(remotePeer.peerId());
        AbortCause reason = peerException.abortCause();
        if (reason != AbortCause.TIMEOUT) {
            if(reason == AbortCause.PROBABLY_OFFLINE) {
                offlineMap.put(remotePeer.peerId(), remotePeer);
            } else if(reason == AbortCause.SHUTDOWN) {
                shutdownMap.put(remotePeer.peerId(), remotePeer);
            } else { // reason is exception
                exceptionMap.put(remotePeer.peerId(), remotePeer);
            }
            Map<Number160, PeerStatistic> tmp = peerMapOverflow.get(classMember);
            if (tmp != null) {
                synchronized (tmp) {
                    tmp.remove(remotePeer.peerId());
                }
            }
            tmp = peerMapVerified.get(classMember);
            if (tmp != null) {
                boolean removed = false;
                final PeerStatistic peerStatatistic;
                synchronized (tmp) {
                    peerStatatistic = tmp.remove(remotePeer.peerId());
                    if (peerStatatistic != null) {
                        removed = true;
                    }
                }
                if (removed) {
                    notifyRemove(remotePeer, peerStatatistic);
                    return true;
                }
            }
            return false;
        }
        // not forced
        if (updatePeerStatistic(remotePeer, peerMapVerified.get(classMember), offlineCount)) {
            return peerFailed(remotePeer, new PeerException(AbortCause.PROBABLY_OFFLINE, "peer failed in verified map"));
        }
        if (updatePeerStatistic(remotePeer, peerMapOverflow.get(classMember), offlineCount)) {
            return peerFailed(remotePeer, new PeerException(AbortCause.PROBABLY_OFFLINE, "peer failed in overflow map"));
        }
        return false;
    }

    /**
     * Checks if a peer address in either in the verified map.
     *
     * @param peerAddress
     *            The peer address to check
     * @return True, if the peer address is either in the verified map
     */
    public boolean contains(final PeerAddress peerAddress) {
        final int classMember = classMember(peerAddress.peerId());
        if (classMember == -1) {
            // -1 means we searched for ourself and we never are our neighbor
            return false;
        }
        final Map<Number160, PeerStatistic> tmp = peerMapVerified.get(classMember);
        synchronized (tmp) {
            return tmp.containsKey(peerAddress.peerId());
        }
    }

    /**
     * Checks if a peer address in either in the overflow / non-verified map.
     *
     * @param peerAddress
     *            The peer address to check
     * @return True, if the peer address is either in the overflow / non-verified map
     */
    public boolean containsOverflow(final PeerAddress peerAddress) {
        final int classMember = classMember(peerAddress.peerId());
        if (classMember == -1) {
            // -1 means we searched for ourself and we never are our neighbor
            return false;
        }
        final Map<Number160, PeerStatistic> tmp = peerMapOverflow.get(classMember);
        synchronized (tmp) {
            return tmp.containsKey(peerAddress.peerId());
        }
    }
   
    /**
     * Returns close peers to the peer itself.
     * @param atLeast The number we want to find at least
     * @return A sorted set with close peers first in this set.
     */
    public NavigableSet<PeerAddress> closePeers(final int atLeast) {
        return closePeers(self, atLeast);
    }

    /**
     * Returns close peer from the set to a given key. This method is tread-safe. You can use the returned set as its a
     * copy of the actual PeerMap and changes in the return set do not affect PeerMap.
     *
     * @param id
     *            The key that should be close to the keys in the map
     * @param atLeast
     *            The number we want to find at least
     * @return A sorted set with close peers first in this set. Use set.first() to get the closest peer
     */
    public NavigableSet<PeerAddress> closePeers(final Number160 id, final int atLeast) {
      return closePeers(self(), id, atLeast, peerMapVerified);
    }

    public static NavigableSet<PeerAddress> closePeers(final Number160 self, final Number160 other, final int atLeast, List<Map<Number160, PeerStatistic>> peerMap) {
        final NavigableSet<PeerAddress> set = new TreeSet<PeerAddress>(createComparator(other));
        final int classMember = classMember(self, other);
        // special treatment, as we can start iterating from 0
        if (classMember == -1) {
            for (int j = 0; j < Number160.BITS; j++) {
                final Map<Number160, PeerStatistic> tmp = peerMap.get(j);
                if (fillSet(atLeast, set, tmp)) {
                    return set;
                }
            }
            return set;
        }

        Map<Number160, PeerStatistic> tmp = peerMap.get(classMember);
        if (fillSet(atLeast, set, tmp)) {
            return set;
        }

        // in this case we have to go over all the bags that are smaller
        boolean last = false;
        for (int i = 0; i < classMember; i++) {
            tmp = peerMap.get(i);
            last = fillSet(atLeast, set, tmp);
        }
        if (last) {
            return set;
        }
        // in this case we have to go over all the bags that are larger
        for (int i = classMember + 1; i < Number160.BITS; i++) {
            tmp = peerMap.get(i);
            fillSet(atLeast, set, tmp);
        }
        return set;
    }
   
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("I'm node ");
        sb.append(self()).append("\n");
        for (int i = 0; i < Number160.BITS; i++) {
            final Map<Number160, PeerStatistic> tmp = peerMapVerified.get(i);
            synchronized (tmp) {
                if (tmp.size() > 0) {
                    sb.append("class:").append(i).append("->\n");
                    for (final PeerStatistic node : tmp.values()) {
                        sb.append("node:").append(node.peerAddress()).append(",");

                    }
                }
            }
        }
        return sb.toString();
    }

    /**
     * Creates an XOR comparator based on this peer ID.
     *
     * @return The XOR comparator
     */
    public Comparator<PeerAddress> createComparator() {
        return createComparator(self);
    }

    /**
     * Return all addresses from the neighbor list. The collection is a copy and it is partially sorted.
     *
     * @return All neighbors
     */
    public List<PeerAddress> all() {
        List<PeerAddress> all = new ArrayList<PeerAddress>();
        for (Map<Number160, PeerStatistic> map : peerMapVerified) {
            synchronized (map) {
                for (PeerStatistic peerStatatistic : map.values()) {
                    all.add(peerStatatistic.peerAddress());
                }
            }
        }
        return all;
    }
   
    public List<Map<Number160, PeerStatistic>> peerMapVerified() {
      return peerMapVerified;
    }
   
    public List<Map<Number160, PeerStatistic>> peerMapOverflow() {
      return peerMapOverflow;
    }

    /**
     * Return all addresses from the overflow / non-verified list. The collection is a copy and it is partially sorted.
     *
     * @return All neighbors
     */
    public List<PeerAddress> allOverflow() {
        List<PeerAddress> all = new ArrayList<PeerAddress>();
        for (Map<Number160, PeerStatistic> map : peerMapOverflow) {
            synchronized (map) {
                for (PeerStatistic peerStatatistic : map.values()) {
                    all.add(peerStatatistic.peerAddress());
                }
            }
        }
        return all;
    }

    /**
     * Checks if a peer is in the offline map.
     *
     * @param peerAddress
     *            The address to look for
     * @return True if the peer is in the offline map, meaning that we consider this peer offline.
     */
    public boolean isPeerRemovedTemporarly(final PeerAddress peerAddress) {
        return offlineMap.containsKey(peerAddress.peerId())
                || shutdownMap.containsKey(peerAddress.peerId())
                || exceptionMap.containsKey(peerAddress.peerId());
    }

    /**
     * Finds the next peer that should have a maintenance check. Returns null if no maintenance is needed at the moment.
     * It will return the most important peers first. Importance is as follows: The most important peers are the close
     * ones in the verified peer map. If a certain threshold in a bag is not reached, the unverified becomes important
     * too.
     *
     * @return The next most important peer to check if its still alive.
     */
    public PeerStatistic nextForMaintenance(Collection<PeerAddress> notInterestedAddresses) {
        return maintenance.nextForMaintenance(notInterestedAddresses);
    }

    /**
     * Returns the number of the class that this id belongs to.
     *
     * @param remoteID
     *            The id to test
     * @return The number of bits used in the difference.
     */
    private int classMember(final Number160 remoteID) {
        return classMember(self(), remoteID);
    }

    /**
     * Returns -1 if the first remote node is closer to the key, if the second is closer, then 1 is returned. If both
     * are equal, 0 is returned
     *
     * @param id
     *            The key to search for
     * @param rn
     *            The remote node on the routing path to node close to key
     * @param rn2
     *            An other remote node on the routing path to node close to key
     * @return -1 if nodeAddress1 is closer to the key than nodeAddress2, otherwise 1. 0 is returned if both are equal.
     */
    public static int isCloser(final Number160 id, final PeerAddress rn, final PeerAddress rn2) {
        return isKadCloser(id, rn, rn2);
    }

    /**
     * Returns -1 if the first key is closer to the key, if the second is closer, then 1 is returned. If both are equal,
     * 0 is returned
     *
     * @param id
     *            The key to search for
     * @param rn
     *            The first key
     * @param rn2
     *            The second key
     * @return -1 if key1 is closer to key, otherwise 1. 0 is returned if both are equal.
     */
    public static int isCloser(final Number160 id, final Number160 rn, final Number160 rn2) {
        return distance(id, rn).compareTo(distance(id, rn2));
    }

    /**
     * @see PeerMap.routing.Routing#isCloser(java.math.BigInteger, PeerAddress.routing.NodeAddress,
     *      PeerAddress.routing.NodeAddress)
     * @param key
     *            The key to search for
     * @param rn2
     *            The remote node on the routing path to node close to key
     * @param rn
     *            An other remote node on the routing path to node close to key
     * @return True if rn2 is closer or has the same distance to key as rn
     */
    /**
     * Returns -1 if the first remote node is closer to the key, if the secondBITS is closer, then 1 is returned. If
     * both are equal, 0 is returned
     *
     * @param id
     *            The id as a distance reference
     * @param rn
     *            The peer to test if closer to the id
     * @param rn2
     *            The other peer to test if closer to the id
     * @return -1 if first peer is closer, 1 otherwise, 0 if both are equal
     */
    public static int isKadCloser(final Number160 id, final PeerAddress rn, final PeerAddress rn2) {
        return distance(id, rn.peerId()).compareTo(distance(id, rn2.peerId()));
    }

    /**
     * Create the Kademlia distance comparator.
     *
     * @param id
     *            The id of this peer
     * @return The XOR comparator
     */
    public static Comparator<PeerAddress> createComparator(final Number160 id) {
        return new Comparator<PeerAddress>() {
            public int compare(final PeerAddress remotePeer, final PeerAddress remotePeer2) {
                return isKadCloser(id, remotePeer, remotePeer2);
            }
        };
    }
   
    public static Comparator<Number160> createComparator2(final Number160 id) {
        return new Comparator<Number160>() {
            public int compare(final Number160 remotePeer, final Number160 remotePeer2) {
                return isCloser(id, remotePeer, remotePeer2);
            }
        };
    }

    /**
     * Returns the difference in terms of bit counts of two ids, minus 1. So two IDs with one bit difference are in the
     * class 0.
     *
     * @param id1
     *            The first id
     * @param id2
     *            The second id
     * @return returns the bit difference and -1 if they are equal
     */
    static int classMember(final Number160 id1, final Number160 id2) {
        return distance(id1, id2).bitLength() - 1;
    }

    /**
     * The distance metric is the XOR metric.
     *
     * @param id1
     *            The first id
     * @param id2
     *            The second id
     * @return The distance
     */
    static Number160 distance(final Number160 id1, final Number160 id2) {
        return id1.xor(id2);
    }

    /**
     * Updates the peer statistics and checks if the max failure has been reached.
     *
     * @param remotePeer
     *            The remote peer
     * @param tmp
     *            The bag of where the peer is supposed to be
     * @param maxFail
     *            The number of max failure until a peer is considered offline
     * @return True if this peer is considered offline, otherwise false
     */
    private static boolean updatePeerStatistic(final PeerAddress remotePeer,
            final Map<Number160, PeerStatistic> tmp, final int maxFail) {
        if (tmp != null) {
            synchronized (tmp) {
                PeerStatistic peerStatatistic = tmp.get(remotePeer.peerId());
                if (peerStatatistic != null) {
                    if (peerStatatistic.failed() >= maxFail) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Checks if a peer already exists in this map and if it does, it will update the entry because the peer address
     * (e.g. port) may have changed.
     *
     * @param tmp
     *            The map where the peer is suppost to be
     * @param peerAddress
     *            The address of the peer that may have been changed
     * @param firstHand
     *            True if this peer send and received a message from the remote peer
     * @return The old peer address if we have updated the peer, null otherwise
     */
    private static PeerStatistic updateExistingVerifiedPeerAddress(
            final Map<Number160, PeerStatistic> tmp, final PeerAddress peerAddress, final boolean firstHand) {
        synchronized (tmp) {
            PeerStatistic old = tmp.get(peerAddress.peerId());
            if (old != null) {
              //TODO: this should only be from firsthand!
                old.peerAddress(peerAddress);
                if (firstHand) {
                    old.successfullyChecked();
                }
                return old;
            }
        }
        return null;
    }

    /**
     * Fills the set with peer addresses. Fills it until a limit is reach. However, this is a soft limit, as the bag may
     * contain close peers in a random manner.
     *
     * @param atLeast
     *            The number of addresses we want at least. It does not matter if its more.
     * @param set
     *            The set where to store the results
     * @param tmp
     *            The bag where to take the addresses from
     * @return True if the desired size has been reached
     */
    private static boolean fillSet(final int atLeast, final SortedSet<PeerAddress> set,
            final Map<Number160, PeerStatistic> tmp) {
        synchronized (tmp) {
            for (final PeerStatistic peerStatatistic : tmp.values()) {
                set.add(peerStatatistic.peerAddress());
            }
        }
        return set.size() >= atLeast;
    }

  public int bagSizeVerified() {
      return bagSizeVerified;
    }
 
  public int bagSizeOverflow() {
      return bagSizeOverflow;
    }

  public PeerAddress find(Number160 peerId) {
    final int classMember = classMember(self, peerId);
    if(classMember < 0) {
      return null;
    }
    Map<Number160, PeerStatistic> tmp = peerMapVerified.get(classMember);
    PeerStatistic peerStatatistic = tmp.get(peerId);
    if(peerStatatistic!=null) {
      return peerStatatistic.peerAddress();
    }
      return null;
    }
}
TOP

Related Classes of net.tomp2p.peers.PeerMap

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.