/* * 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.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import net.tomp2p.peers.PeerAddress; import net.tomp2p.peers.PeerSocketAddress.PeerSocket4Address; /** * The future that keeps track of network discovery such as discovery if its behind a NAT, the status if UPNP or NAT-PMP * could be established, if there is portforwarding. * * @author Thomas Bocek */ public class FutureDiscover extends BaseFutureImpl<FutureDiscover> { // result private PeerAddress ourPeerAddress; private PeerAddress reporter; private boolean discoveredTCP = false; private boolean discoveredUDP = false; private boolean isNat = false; private PeerSocket4Address internalAddress; private PeerSocket4Address externalAddress; /** * Constructor. */ public FutureDiscover() { self(this); } /** * Creates a new future object and creates a timer that fires failed after a timeout. * * @param timer * The timer to use * @param delaySec * The delay in seconds */ public void timeout(final PeerAddress serverPeerAddress, final ScheduledExecutorService timer, final int delaySec) { final DiscoverTimeoutTask task = new DiscoverTimeoutTask(serverPeerAddress); final ScheduledFuture<?> scheduledFuture = timer.schedule(task, TimeUnit.SECONDS.toMillis(delaySec), TimeUnit.MILLISECONDS); // cancel timeout if we are done addListener(new BaseFutureAdapter<FutureDiscover>() { @Override public void operationComplete(final FutureDiscover future) throws Exception { scheduledFuture.cancel(false); } }); } /** * Gets called if the discovery was a success and an other peer could ping us with TCP and UDP. * * @param ourPeerAddress * The peerAddress of our server * @param reporter * The peerAddress of the peer that reported our address */ public void done(final PeerAddress ourPeerAddress, final PeerAddress reporter) { synchronized (lock) { if (!completedAndNotify()) { return; } this.type = FutureType.OK; if(this.reporter != null) { if(!this.reporter.equals(reporter)) { this.type = FutureType.FAILED; this.reason = "the previously reported peer ("+this.reporter+") does not match the peer reported now ("+reporter+")"; } } this.ourPeerAddress = ourPeerAddress; this.reporter = reporter; } notifyListeners(); } /** * The peerAddress where we are reachable. * * @return The new un-firewalled peerAddress of this peer */ public PeerAddress peerAddress() { synchronized (lock) { return ourPeerAddress; } } /** * @return The reporter that told us what peer address we have */ public PeerAddress reporter() { synchronized (lock) { return reporter; } } public FutureDiscover reporter(PeerAddress reporter) { synchronized (lock) { if(this.reporter != null) { if(!this.reporter.equals(reporter)) { throw new IllegalArgumentException("Cannot change reporter once it is set."); } } this.reporter = reporter; } return this; } /** * Intermediate result if TCP has been discovered. Set discoveredTCP True if other peer could reach us with a TCP * ping. */ public void discoveredTCP() { synchronized (lock) { this.discoveredTCP = true; } } /** * Intermediate result if UDP has been discovered. Set discoveredUDP True if other peer could reach us with a UDP * ping. */ public void discoveredUDP() { synchronized (lock) { this.discoveredUDP = true; } } /** * Checks if this peer can be reached via TCP. * * @return True if this peer can be reached via TCP from outside. */ public boolean isDiscoveredTCP() { synchronized (lock) { return discoveredTCP; } } /** * Checks if this peer can be reached via UDP. * * @return True if this peer can be reached via UDP from outside. */ public boolean isDiscoveredUDP() { synchronized (lock) { return discoveredUDP; } } /** * In case of no peer can contact us, we fire an failed. */ private final class DiscoverTimeoutTask implements Runnable { private final long start = System.currentTimeMillis(); private final PeerAddress serverPeerAddress; private DiscoverTimeoutTask(PeerAddress serverPeerAddress) { this.serverPeerAddress = serverPeerAddress; } @Override public void run() { failed(serverPeerAddress, "Timeout in Discover: " + (System.currentTimeMillis() - start) + "ms. Seems like pingTCPProbe or pingUDPProbe did not " + "succeed in time. However my address reported from pingTCPDiscover is " + serverPeerAddress); } } public FutureDiscover failed(PeerAddress serverPeerAddress, final String failed) { synchronized (lock) { if (!completedAndNotify()) { return this; } this.reason = failed; this.ourPeerAddress = serverPeerAddress; this.type = FutureType.FAILED; } notifyListeners(); return this; } public FutureDiscover externalHost(String failed, PeerSocket4Address internalAddress, PeerSocket4Address externalAddress) { synchronized (lock) { if (!completedAndNotify()) { return this; } this.reason = failed; this.type = FutureType.FAILED; this.internalAddress = internalAddress; this.externalAddress = externalAddress; this.isNat = true; } notifyListeners(); return this; } public PeerSocket4Address internalAddress() { synchronized (lock) { return internalAddress; } } public PeerSocket4Address externalAddress() { synchronized (lock) { return externalAddress; } } public boolean isNat() { synchronized (lock) { return isNat; } } }