/* * Copyright 2011 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.futures; import java.util.NavigableSet; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeSet; import net.tomp2p.peers.PeerAddress; import net.tomp2p.rpc.DigestInfo; /** * The routing future keeps track of the routing process. This means that the * routing future is returned immediately and the routing process starts in the * background. There are two ways to wait for the routing process: (1) to use * await*, which blocks the current thread and waits for the routing process to * complete, or (2) to use addListener*, which will be called when the routing * process completes. The listener may or may not run in the same thread. The * routing will always succeed if we do DHT operations or bootstrap to ourself. * It will fail if we bootstrap to another peer, but could not contact any peer * than ourself. * * @see #setNeighbors(SortedSet, SortedSet, SortedSet, boolean, boolean) * @author Thomas Bocek */ public class FutureRouting extends BaseFutureImpl<FutureRouting> { private NavigableSet<PeerAddress> potentialHits; private SortedMap<PeerAddress, DigestInfo> directHits; private SortedSet<PeerAddress> routingPath; public FutureRouting() { self(this); } /** * Sets the result of the routing process and finishes the future. This will * notify all listeners. The future will always succeed if we do DHT * operations or bootstrap to ourself. It will fail if we bootstrap to * another peer, but could not contact any peer than ourself. * * @see #getDirectHits() * @see #getDirectHitsDigest() * @see #getPotentialHits() * @see #getRoutingPath() * @param directHits * The direct hits, the peers in the direct set that reports to * have the key (Number160) we were looking for. * @param potentialHits * The potential hits, the peers in the direct set and those * peers that reports to *not* have the key (Number160) we were * looking for. * @param routingPath * A set of peers that took part in the routing process. * @param isBootstrap * Whether the future was triggered by the bootstrap process or * the a P2P process * @param isRoutingToOther * Whether routing peers have been specified others than myself. */ public void setNeighbors(final SortedMap<PeerAddress, DigestInfo> directHits, final NavigableSet<PeerAddress> potentialHits, final SortedSet<PeerAddress> routingPath, boolean isBootstrap, boolean isRoutingToOther) { synchronized (lock) { if (!setCompletedAndNotify()) return; this.potentialHits = potentialHits; this.directHits = directHits; this.routingPath = routingPath; if (isBootstrap && isRoutingToOther) { // we need to fail if we only find ourself. This means that we // did not connect to any peer and we did // not // wanted connect to ourself. this.type = ((potentialHits.size() <= 1) && (directHits.size() == 0)) ? BaseFuture.FutureType.FAILED : BaseFuture.FutureType.OK; } else { // for DHT or bootstraping to ourself, we set to success, since // we may want to store // data on our peer rather than failing completely if we dont // find other peers this.type = BaseFuture.FutureType.OK; } } notifyListerenrs(); } /** * The potential hits set contains those peers that are in the direct hit * and that did report to *not* have the key (Number160) we were looking * for. We already check for the content during routing, since we send the * information what we are looking for anyway, so a reply if the content * exists or not is not very expensive. However, a peer may lie about this. * * @see #getDirectHits() * @see #getDirectHitsDigest() * @return The potential hits, the peers in the direct set and those peers * that reports to *not* have the key (Number160) we were looking * for. */ public NavigableSet<PeerAddress> getPotentialHits() { synchronized (lock) { return potentialHits; } } /** * The direct hits set contains those peers that reported to have the key * (Number160) we were looking for. We already check for the content during * routing, since we send the information what we are looking for anyway, so * a reply if the content exists or not is not very expensive. However, a * peer may lie about this. * * @see #getPotentialHits() * @see #getDirectHitsDigest() * @return The direct hits, the peers in the direct set that reports to have * the key (Number160) we were looking for. */ public NavigableSet<PeerAddress> getDirectHits() { synchronized (lock) { if (directHits == null) { return null; } // some Java implementations always return SortedSet, some don't. Set<PeerAddress> tmp = directHits.keySet(); // if we have a NavigableSet, we are fine if (tmp instanceof NavigableSet) { return (NavigableSet<PeerAddress>) directHits.keySet(); } // otherwise, create a new sorted set, put the existing values // there, and return this. else { NavigableSet<PeerAddress> tmp2 = new TreeSet<PeerAddress>(directHits.comparator()); tmp2.addAll(tmp); return tmp2; } } } /** * The direct hits map contains those peers that reported to have the key * (Number160) we were looking for including its digest (size of the result * set and its xored hashes). We already check for the content during * routing, since we send the information what we are looking for anyway, so * a reply if the content exists or not is not very expensive. However, a * peer may lie about this. * * @see #getPotentialHits() * @see #getDirectHits() * @return The direct hits including its digest (size of the result set and * its xored hashes), when a peer reports to have the key * (Number160) we were looking for. */ public SortedMap<PeerAddress, DigestInfo> getDirectHitsDigest() { synchronized (lock) { return directHits; } } /** * Returns the peers that have been asked to provide neighbor information. * The order is sorted by peers that were close to the target. * * @return A set of peers that took part in the routing process. */ public SortedSet<PeerAddress> getRoutingPath() { synchronized (lock) { return routingPath; } } @Override public String getFailedReason() { synchronized (lock) { return "FutureRouting -> complete:" + completed + ", type:" + type.toString() + ", direct:" + directHits.size() + ", neighbors:" + potentialHits.size(); } } }