Java tutorial
/** * Copyright (C) 2011-2012 Turn, Inc. * * 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 com.turn.ttorrent.tracker; import com.google.common.base.Objects; import com.google.common.collect.Iterables; import com.turn.ttorrent.protocol.TorrentUtils; import java.net.InetSocketAddress; import java.util.HashSet; import java.util.Set; import javax.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A BitTorrent tracker peer. * * <p> * Represents a peer exchanging on a given torrent. In this implementation, * we don't really care about the status of the peers and how much they * have downloaded / exchanged because we are not a torrent exchange and * don't need to keep track of what peers are doing while they're * downloading. We only care about when they start, and when they are done. * </p> * * <p> * We also never expire peers automatically. Unless peers send a STOPPED * announce request, they remain as long as the torrent object they are a * part of. * </p> */ public class TrackedPeer { private static final Logger LOG = LoggerFactory.getLogger(TrackedPeer.class); private final byte[] peerId; private final Set<InetSocketAddress> peerAddresses = new HashSet<InetSocketAddress>(); private TrackedPeerState state = TrackedPeerState.UNKNOWN; private long uploaded = 0; private long downloaded = 0; private long left = 0; private long lastAnnounce = System.currentTimeMillis(); // We need a happen-before relationship for multiple threads. private final Object lock = new Object(); /** * Instantiate a new tracked peer for the given torrent. * * @param peer The remote peer. * @param torrent The torrent this peer exchanges on. */ public TrackedPeer(@Nonnull byte[] peerId) { this.peerId = peerId; } @Nonnull public byte[] getPeerId() { return peerId; } @Nonnull public String getPeerName() { return TorrentUtils.toText(getPeerId()); } @Nonnull public Set<? extends InetSocketAddress> getPeerAddresses() { return peerAddresses; } /** * Update this peer's state and information. * * <p> * <b>Note:</b> if the peer reports 0 bytes left to download, its state will * be automatically be set to COMPLETED. * </p> * * @param torrent The torrent. This should be the same on every call. * @param state The peer's state. * @param uploaded Uploaded byte count, as reported by the peer. * @param downloaded Downloaded byte count, as reported by the peer. * @param left Left-to-download byte count, as reported by the peer. */ public void update(@Nonnull TrackedTorrent torrent, TrackedPeerState state, Iterable<? extends InetSocketAddress> peerAddresses, long uploaded, long downloaded, long left) { if (TrackedPeerState.STARTED.equals(state) && left == 0) state = TrackedPeerState.COMPLETED; if (!state.equals(this.state)) { if (LOG.isDebugEnabled()) LOG.debug("Peer {} {} download of {}.", new Object[] { this, state.name().toLowerCase(), torrent }); } synchronized (lock) { Iterables.addAll(this.peerAddresses, peerAddresses); this.state = state; this.uploaded = uploaded; this.downloaded = downloaded; this.left = left; this.lastAnnounce = System.currentTimeMillis(); } } /** * Tells whether this peer has completed its download and can thus be * considered a seeder. */ public boolean isCompleted() { return TrackedPeerState.COMPLETED.equals(this.state); } /** * Returns how many bytes the peer reported it has uploaded so far. */ public long getUploaded() { return this.uploaded; } /** * Returns how many bytes the peer reported it has downloaded so far. */ public long getDownloaded() { return this.downloaded; } /** * Returns how many bytes the peer reported it needs to retrieve before * its download is complete. */ public long getLeft() { return this.left; } /** * Tells whether this peer has checked in with the tracker recently. * * <p> * Non-fresh peers are automatically terminated and collected by the * Tracker. * </p> * * @param now The current time. * @param refresh The interval after which a peer is considered stale. */ public boolean isFresh(long now, long refresh) { synchronized (lock) { return (this.lastAnnounce > 0 && (this.lastAnnounce + refresh > now)); } } @Override public String toString() { return Objects.toStringHelper(this).add("name", getPeerName()).add("addresses", getPeerAddresses()) .toString(); } }