package org.limewire.mojito.manager;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import junit.framework.TestSuite;
import org.limewire.mojito.Context;
import org.limewire.mojito.KUID;
import org.limewire.mojito.MojitoDHT;
import org.limewire.mojito.MojitoFactory;
import org.limewire.mojito.MojitoTestCase;
import org.limewire.mojito.exceptions.DHTTimeoutException;
import org.limewire.mojito.io.MessageDispatcher;
import org.limewire.mojito.io.MessageDispatcherFactory;
import org.limewire.mojito.io.MessageDispatcherImpl;
import org.limewire.mojito.io.Tag;
import org.limewire.mojito.messages.DHTMessage;
import org.limewire.mojito.messages.FindNodeRequest;
import org.limewire.mojito.messages.PingRequest;
import org.limewire.mojito.result.BootstrapResult;
import org.limewire.mojito.result.PingResult;
import org.limewire.mojito.routing.Contact;
import org.limewire.mojito.routing.ContactFactory;
import org.limewire.mojito.routing.RouteTable;
import org.limewire.mojito.routing.Vendor;
import org.limewire.mojito.routing.Version;
import org.limewire.mojito.settings.BootstrapSettings;
import org.limewire.mojito.settings.NetworkSettings;
public class BootstrapManagerTest extends MojitoTestCase {
/*static {
System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog");
}*/
protected static final int BOOTSTRAP_DHT_PORT = 3000;
protected static MojitoDHT BOOTSTRAP_DHT;
protected static MojitoDHT TEST_DHT;
private TestMessageDispatcherFactory tmf, bmf;
public BootstrapManagerTest(String name){
super(name);
}
public static TestSuite suite() {
return buildTestSuite(BootstrapManagerTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
private static void setSettings() {
NetworkSettings.DEFAULT_TIMEOUT.setValue(200);
NetworkSettings.MIN_TIMEOUT_RTT.setValue(200);
}
@Override
protected void setUp() throws Exception {
super.setUp();
setSettings();
setLocalIsPrivate(false);
//setup bootstrap node
BOOTSTRAP_DHT = MojitoFactory.createDHT("bootstrapNode");
bmf = new TestMessageDispatcherFactory((Context)BOOTSTRAP_DHT);
BOOTSTRAP_DHT.setMessageDispatcher(bmf);
BOOTSTRAP_DHT.bind(BOOTSTRAP_DHT_PORT);
BOOTSTRAP_DHT.start();
//setup test node
TEST_DHT = MojitoFactory.createDHT("dht-test");
tmf = new TestMessageDispatcherFactory((Context)TEST_DHT);
TEST_DHT.setMessageDispatcher(tmf);
TEST_DHT.bind(2000);
TEST_DHT.start();
}
@Override
protected void tearDown() throws Exception {
if (BOOTSTRAP_DHT != null) {
BOOTSTRAP_DHT.close();
}
if (TEST_DHT != null) {
TEST_DHT.close();
}
}
public void testBootstrapFailure() throws Exception {
((Context)BOOTSTRAP_DHT).setExternalAddress(
new InetSocketAddress("localhost", BOOTSTRAP_DHT_PORT));
// try failure first
BOOTSTRAP_DHT.stop();
try {
((Context)TEST_DHT).ping(BOOTSTRAP_DHT.getLocalNode()).get();
fail("BOOTSTRAP_DHT should not respond");
} catch (ExecutionException err) {
assertTrue(err.getCause() instanceof DHTTimeoutException);
}
// made sure we tried to ping them
assertGreaterThan(1, tmf.tm.sent.size());
Tag sent = tmf.tm.sent.get(0);
assertEquals(sent.getNodeID(), BOOTSTRAP_DHT.getLocalNodeID());
assertInstanceof(PingRequest.class, sent.getMessage());
BootstrapResult result = TEST_DHT.bootstrap(BOOTSTRAP_DHT.getLocalNode()).get();
assertEquals(BootstrapResult.ResultType.BOOTSTRAP_FAILED, result.getResultType());
assertEquals(BOOTSTRAP_DHT.getLocalNodeID(), result.getContact().getNodeID());
BOOTSTRAP_DHT.close();
}
public void testBootstrapFromList() throws Exception{
//try pings to a bootstrap list
//add some bad hosts
Set<SocketAddress> bootstrapSet = new LinkedHashSet<SocketAddress>();
bootstrapSet.clear();
for(int i= 1; i < 5; i++) {
bootstrapSet.add(new InetSocketAddress("localhost", BOOTSTRAP_DHT_PORT+i));
}
//add good host
bootstrapSet.add(BOOTSTRAP_DHT.getContactAddress());
PingResult pong = ((Context)TEST_DHT).ping(bootstrapSet).get();
BootstrapResult result = TEST_DHT.bootstrap(pong.getContact()).get();
assertEquals(result.getResultType(), BootstrapResult.ResultType.BOOTSTRAP_SUCCEEDED);
assertEquals(BOOTSTRAP_DHT.getLocalNodeID(), result.getContact().getNodeID());
}
public void testBootstrapFromRouteTable() throws Exception{
//try ping from RT
RouteTable rt = TEST_DHT.getRouteTable();
Contact node;
for(int i= 1; i < 5; i++) {
node = ContactFactory.createLiveContact(null, Vendor.UNKNOWN, Version.ZERO,
KUID.createRandomID(),
new InetSocketAddress("localhost", 3000+i),
0, Contact.DEFAULT_FLAG);
rt.add(node);
}
//add good node
node = ContactFactory.createLiveContact(null, Vendor.UNKNOWN, Version.ZERO,
BOOTSTRAP_DHT.getLocalNodeID(),
BOOTSTRAP_DHT.getContactAddress(), 0, Contact.DEFAULT_FLAG);
rt.add(node);
// Start pinging Nodes from the RouteTable
PingResult pong = TEST_DHT.findActiveContact().get();
// And bootstrap from the first Node that responds
BootstrapResult evt = TEST_DHT.bootstrap(pong.getContact()).get();
assertEquals(evt.getResultType(), BootstrapResult.ResultType.BOOTSTRAP_SUCCEEDED);
}
public void testBootstrapPoorRatio() throws Exception{
//fill RT with bad nodes
RouteTable rt = TEST_DHT.getRouteTable();
Contact node;
for(int i = 0; i < 100; i++) {
node = ContactFactory.createUnknownContact(Vendor.UNKNOWN, Version.ZERO,
KUID.createRandomID(),
new InetSocketAddress("localhost", 3000+i));
rt.add(node);
}
node = ContactFactory.createUnknownContact(Vendor.UNKNOWN, Version.ZERO,
KUID.createRandomID(),
new InetSocketAddress("localhost", 7777));
rt.add(node);
assertEquals(102, rt.size());
PingResult pong = TEST_DHT.ping(BOOTSTRAP_DHT.getContactAddress()).get();
BootstrapResult result = TEST_DHT.bootstrap(pong.getContact()).get();
// See if RT was purged
assertNotContains(rt.getActiveContacts(), node);
assertEquals(result.getResultType(), BootstrapResult.ResultType.BOOTSTRAP_SUCCEEDED);
}
public void testBootstrapPoorRatioFails() throws Exception{
BootstrapSettings.IS_BOOTSTRAPPED_RATIO.setValue(0.7f);
//fill RT with bad nodes
RouteTable rt = TEST_DHT.getRouteTable();
Contact node;
for(int i = 0; i < 100; i++) {
node = ContactFactory.createUnknownContact(Vendor.UNKNOWN, Version.ZERO,
KUID.createRandomID(),
new InetSocketAddress("localhost", 3000+i));
rt.add(node);
}
node = ContactFactory.createUnknownContact(Vendor.UNKNOWN, Version.ZERO,
KUID.createRandomID(),
new InetSocketAddress("localhost", 7777));
rt.add(node);
// we lose the find node requests
bmf.tm.filter = new FindNodeFilter(1.0);
assertEquals(102, rt.size());
PingResult pong = TEST_DHT.ping(BOOTSTRAP_DHT.getContactAddress()).get();
BootstrapResult result = TEST_DHT.bootstrap(pong.getContact()).get();
assertEquals(result.getResultType(), BootstrapResult.ResultType.BOOTSTRAP_FAILED);
}
public void testBootstrapPoorRatioSucceeds() throws Exception{
BootstrapSettings.IS_BOOTSTRAPPED_RATIO.setValue(0.1f);
//fill RT with bad nodes
RouteTable rt = TEST_DHT.getRouteTable();
Contact node;
for(int i = 0; i < 100; i++) {
node = ContactFactory.createUnknownContact(Vendor.UNKNOWN, Version.ZERO,
KUID.createRandomID(),
new InetSocketAddress("localhost", 3000+i));
rt.add(node);
}
node = ContactFactory.createUnknownContact(Vendor.UNKNOWN, Version.ZERO,
KUID.createRandomID(),
new InetSocketAddress("localhost", 7777));
rt.add(node);
// we lose the find node requests
bmf.tm.filter = new FindNodeFilter(1.0);
assertEquals(102, rt.size());
PingResult pong = TEST_DHT.ping(BOOTSTRAP_DHT.getContactAddress()).get();
BootstrapResult result = TEST_DHT.bootstrap(pong.getContact()).get();
assertEquals(result.getResultType(), BootstrapResult.ResultType.BOOTSTRAP_SUCCEEDED);
}
private static class TestMessageDispatcherFactory implements MessageDispatcherFactory {
final TestMessageDispatcher tm;
public TestMessageDispatcherFactory(Context context) {
tm = new TestMessageDispatcher(context);
}
public MessageDispatcher create(Context context) {
return tm;
}
}
private static class TestMessageDispatcher extends MessageDispatcherImpl {
List<Tag> sent = new ArrayList<Tag>();
List<Tag> notSent = new ArrayList<Tag>();
List<DHTMessage> received = new ArrayList<DHTMessage>();
List<DHTMessage> notReceived = new ArrayList<DHTMessage>();
volatile TestFilter filter;
public TestMessageDispatcher(Context context) {
super(context);
}
@Override
protected boolean send(Tag tag) throws IOException {
TestFilter f = filter;
if (f != null && !f.allow(tag.getMessage())) {
notSent.add(tag);
return false;
}
sent.add(tag);
return super.send(tag);
}
@Override
protected void handleMessage(DHTMessage m) {
received.add(m);
super.handleMessage(m);
}
}
private static interface TestFilter {
boolean allow(DHTMessage m);
}
private static class FindNodeFilter implements TestFilter {
private final double prob;
FindNodeFilter(double prob) {
this.prob = prob;
}
public boolean allow(DHTMessage m) {
return !(m instanceof FindNodeRequest && Math.random() < prob);
}
}
}