package freenet.node; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import freenet.node.PeerNode; /** * For any single UID, we should only route to the same node once, even if * the request comes back to us in a loop. Loop detection with rejection is * dangerous, and we can't easily disentangle the various failure modes, so * we allow a request to loop back to us and then relay it forward, RNFing * if appropriate. So we keep a UIDRoutingContext for each UID that has live * UIDTag's. * * @author toad */ public class UIDRoutingContextTracker { private final HashMap<Long,UIDRoutingContext> routingContexts = new HashMap<Long,UIDRoutingContext>(); public class UIDRoutingContext { /** The UID of all the tags */ public final Long uid; /** If true, tags will be for inserts, and no offer tags. */ public final boolean insert; /** Request or insert tags */ private final ArrayList<UIDTag> tags; /** The set of nodes this UID has been routed to. This will always * be very small. */ private final Set<WeakReference<PeerNode>> routedTo; private boolean finished; UIDRoutingContext(long uid, boolean insert) { this.uid = uid; this.insert = insert; tags = new ArrayList<UIDTag>(); routedTo = new HashSet<WeakReference<PeerNode>>(); finished = false; } public void add(UIDTag tag) { synchronized(UIDRoutingContextTracker.this) { if(finished) throw new IllegalStateException(); if(!tags.contains(tag)) tags.add(tag); } } public void remove(UIDTag tag) { synchronized(UIDRoutingContextTracker.this) { tags.remove(tag); if(tags.isEmpty()) removeMe(); } } private void removeMe() { finished = true; routingContexts.remove(uid); } } }