package com.limegroup.gnutella.http; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.limewire.collection.MultiRRIterator; import com.limegroup.gnutella.URN; import com.limegroup.gnutella.altlocs.AltLocManager; import com.limegroup.gnutella.altlocs.AlternateLocation; import com.limegroup.gnutella.altlocs.AlternateLocationCollection; import com.limegroup.gnutella.altlocs.DirectAltLoc; import com.limegroup.gnutella.altlocs.PushAltLoc; /** * Manages a list of alternate locations. */ public class AltLocTracker { /** * The alternate locations that have been written out (as good) locations. */ private Set<DirectAltLoc> writtenLocs = new HashSet<DirectAltLoc>(); /** * The firewalled alternate locations that have been written out as good * locations. */ private Set<PushAltLoc> writtenPushLocs = new HashSet<PushAltLoc>(); /** * The maximum number of alts to write per http transfer. */ private static final int MAX_LOCATIONS = 10; /** * The maximum number of firewalled alts to write per http transfer. */ private static final int MAX_PUSH_LOCATIONS = 5; /** * The version of the FWT protocol the remote supports. Non-firewalled hosts * should not send this feature. INVARIANT: if this is greater than 0, * wantsFalts is set. */ private int fwtVersion = 0; private final URN urn; private boolean wantsFAlts; public AltLocTracker(URN urn) { if (urn == null) { throw new IllegalArgumentException(); } this.urn = urn; } /** * Returns an AlternateLocationCollection of alternates that have not been * sent out already. */ public Collection<DirectAltLoc> getNextSetOfAltsToSend(AltLocManager altLocManager) { AlternateLocationCollection<DirectAltLoc> coll = altLocManager.getDirect(urn); Collection<DirectAltLoc> ret = null; long now = System.currentTimeMillis(); synchronized (coll) { Iterator<DirectAltLoc> iter = coll.iterator(); for (int i = 0; iter.hasNext() && i < MAX_LOCATIONS;) { DirectAltLoc al = iter.next(); if (writtenLocs.contains(al)) continue; if (al.canBeSent(AlternateLocation.MESH_LEGACY)) { writtenLocs.add(al); if (ret == null) ret = new HashSet<DirectAltLoc>(); ret.add(al); i++; al.send(now, AlternateLocation.MESH_LEGACY); } else if (!al.canBeSentAny()) iter.remove(); } } if (ret == null) return Collections.emptySet(); else return ret; } public Collection<PushAltLoc> getNextSetOfPushAltsToSend(AltLocManager altLocManager) { if (!wantsFAlts) return Collections.emptySet(); AlternateLocationCollection<PushAltLoc> fwt = altLocManager.getPushFWT(urn); AlternateLocationCollection<PushAltLoc> push; if (fwtVersion > 0) push = AlternateLocationCollection.getEmptyCollection(); else push = altLocManager.getPushNoFWT(urn); Collection<PushAltLoc> ret = null; long now = System.currentTimeMillis(); synchronized (push) { synchronized (fwt) { Iterator<PushAltLoc> iter = new MultiRRIterator<PushAltLoc>(fwt .iterator(), push.iterator()); for (int i = 0; iter.hasNext() && i < MAX_PUSH_LOCATIONS;) { PushAltLoc al = iter.next(); if (writtenPushLocs.contains(al)) continue; // it is possible to end up having a PE with all // proxies removed. In that case we remove it explicitly if (al.getPushAddress().getProxies().isEmpty()) { iter.remove(); continue; } if (al.canBeSent(AlternateLocation.MESH_LEGACY)) { al.send(now, AlternateLocation.MESH_LEGACY); writtenPushLocs.add(al); if (ret == null) ret = new HashSet<PushAltLoc>(); ret.add(al); i++; } else if (!al.canBeSentAny()) iter.remove(); } } } if (ret == null) return Collections.emptySet(); else return ret; } public void addLocation(AlternateLocation al) { if (al instanceof DirectAltLoc) writtenLocs.add((DirectAltLoc) al); else writtenPushLocs.add((PushAltLoc) al); // no problem if we add an // existing pushloc } public boolean wantsFAlts() { return wantsFAlts; } public void setWantsFAlts(boolean wantsFAlts) { this.wantsFAlts = wantsFAlts; } public URN getUrn() { return urn; } public int getFwtVersion() { return fwtVersion; } public void setFwtVersion(int fwtVersion) { this.fwtVersion = fwtVersion; } }