/*
* 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();
}
}
}