/* * Copyright 2009 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.Iterator; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import net.tomp2p.utils.FIFOCache; /** * Keeps track of the statistics of a given peer. * * @author Thomas Bocek * */ public class PeerStatistic { public static final int RTT_CACHE_SIZE = 5; private final AtomicLong lastSeenOnline = new AtomicLong(0); private final long created = System.currentTimeMillis(); private final AtomicInteger successfullyChecked = new AtomicInteger(0); private final AtomicInteger failed = new AtomicInteger(0); private final Number160 peerId; private PeerAddress peerAddress; private boolean local; private FIFOCache<RTT> rttCache = new FIFOCache<RTT>(RTT_CACHE_SIZE); private long numberOfResponses = 0; /** * Constructor. Sets the peer address * * @param peerAddress * The peer address that belongs to this statistics */ public PeerStatistic(final PeerAddress peerAddress) { if (peerAddress == null) { throw new IllegalArgumentException("PeerAddress cannot be null."); } this.peerId = peerAddress.peerId(); this.peerAddress = peerAddress; } /** * Sets the time when last seen online to now. * * @return The number of successful checks. */ public int successfullyChecked() { lastSeenOnline.set(System.currentTimeMillis()); failed.set(0); return successfullyChecked.incrementAndGet(); } /** * Gets the time the peer has last been seen online. * * @return The time the peer has last been seen online. */ public long lastSeenOnline() { return lastSeenOnline.get(); } /** * Gets the number of times the peer has been successfully checked. * * @return The number of times the peer has been successfully checked. */ public int successfullyCheckedCounter() { return successfullyChecked.get(); } /** * Increases the failed counter. * * @return The number of failed checks. */ public int failed() { return failed.incrementAndGet(); } /** * @return The time of this PeerStatistic creation. */ public long created() { return created; } /** * @return The time that this peer is online. */ public int onlineTime() { return (int) (lastSeenOnline.get() - created); } public long getNumberOfResponses() { return numberOfResponses; } public void increaseNumberOfResponses() { numberOfResponses++; } /** * @return The PeerAddress associated with this peer. */ public PeerAddress peerAddress() { return peerAddress; } /** * Sets a new PeerAddress, but only if the previous had the same peer ID. * * @param peerAddress * The new peer address. * @return The old peer address. */ public PeerAddress peerAddress(final PeerAddress peerAddress) { if (!peerId.equals(peerAddress.peerId())) { throw new IllegalArgumentException("Can only update PeerAddress with the same peer ID."); } PeerAddress previousPeerAddress = this.peerAddress; this.peerAddress = peerAddress; return previousPeerAddress; } public PeerStatistic local() { setLocal(true); return this; } public boolean isLocal() { return local; } public PeerStatistic setLocal(boolean local) { this.local = local; return this; } /** * Adds a RTT to the cache. If the provided RTT object is an estimate * it will be ignored in case there are already first-hand measurements * in the cache. Also any real measurement will clear out any estimates * in the cache beforehand. This prevents any mix-up between real * measurements and estimates * * @param rtt The RTT object that should be added * @return The PeerStatistic object */ public PeerStatistic addRTT(RTT rtt) { if (rtt != null && rtt.getRtt() > 0) { // If we have estimates in the cache, // clear cache before adding "real" measurement if (containsEstimates()) { if (!rtt.isEstimated()) rttCache.clear(); rttCache.add(rtt); return this; // Only add measured RTTs to our cache if we have some already // Estimates will be ignored after we received at least 1 "real" RTT } else if (!rtt.isEstimated() || rttCache.isEmpty()) { rttCache.add(rtt); return this; } } return this; } /** * @return True, if the RTT cache contains estimates, false if empty or * cache is empty. */ public boolean containsEstimates() { return !rttCache.isEmpty() && rttCache.peek().isEstimated(); } /** * @return Most recent RTT or null if cache is empty */ public RTT getLatestRTT() { if (rttCache.isEmpty()) return null; return rttCache.peekTail(); } /** * Get the mean of the last 5 RTTs * * @return Average RTT in milliseconds or -1 if cache is empty. */ public long getMeanRTT() { if (rttCache.isEmpty()) return -1; long sum = 0; for (Iterator<RTT> iterator = rttCache.iterator(); iterator.hasNext(); ) { RTT next = iterator.next(); sum += next.getRtt(); } return sum / rttCache.size(); } /** * How many RTT measurements are in the cache * * @return Number of RTT objects in cache. Number between * 0 and RTT_CACHE_SIZE */ public int getRTTCount() { return rttCache.size(); } @Override public int hashCode() { return peerId.hashCode(); } @Override public boolean equals(Object obj) { if(obj == this) { return true; } if(!(obj instanceof PeerStatistic)) { return false; } PeerStatistic p = (PeerStatistic) obj; return p.peerId.equals(peerId); } }