package com.limegroup.gnutella.dht; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.InetSocketAddress; import java.util.Collections; import java.util.List; import org.limewire.core.settings.DHTSettings; import org.limewire.io.IOUtils; import org.limewire.io.IpPort; import org.limewire.io.SecureInputStream; import org.limewire.io.SecureOutputStream; import org.limewire.mojito.MojitoDHT; import org.limewire.mojito.MojitoFactory; import org.limewire.mojito.db.Database; import org.limewire.mojito.routing.RouteTable; import org.limewire.mojito.routing.Vendor; import org.limewire.mojito.routing.Version; import org.limewire.util.CommonUtils; import com.limegroup.gnutella.connection.Connection; import com.limegroup.gnutella.connection.ConnectionLifecycleEvent; import com.limegroup.gnutella.dht.DHTManager.DHTMode; import com.limegroup.gnutella.util.EventDispatcher; /** * Controls an active DHT node. * <p> * Active nodes load and persist DHT data, as they keep the same node ID * over multiple sessions and can therefore re-use their local routing table * and local database. */ public class ActiveDHTNodeController extends AbstractDHTController { /** * The file to persist this Mojito DHT */ private static final File FILE = new File(CommonUtils.getUserSettingsDir(), "active.mojito"); ActiveDHTNodeController(Vendor vendor, Version version, EventDispatcher<DHTEvent, DHTEventListener> dispatcher, DHTControllerFacade dhtControllerFacade) { super(vendor, version, dispatcher, DHTMode.ACTIVE, dhtControllerFacade); } @Override protected MojitoDHT createMojitoDHT(Vendor vendor, Version version) { MojitoDHT dht = MojitoFactory.createDHT("ActiveMojitoDHT", vendor, version); if (DHTSettings.PERSIST_ACTIVE_DHT_ROUTETABLE.getValue() && FILE.exists() && FILE.isFile()) { ObjectInputStream in = null; try { in = new ObjectInputStream( new BufferedInputStream( new SecureInputStream( new FileInputStream(FILE)))); // Pre-condition: The Database depends on the RouteTable // If there's no persisted RouteTable or it's corrupt // then there's no point in reading or setting the // Database! int routeTableVersion = in.readInt(); if (routeTableVersion >= getRouteTableVersion()) { RouteTable routeTable = (RouteTable)in.readObject(); Database database = null; try { if (DHTSettings.PERSIST_DHT_DATABASE.getValue()) { database = (Database)in.readObject(); } } catch (Throwable ignored) { LOG.error("Throwable", ignored); } // The Database depends on the RouteTable! if (routeTable != null) { long maxElaspedTime = DHTSettings.MAX_ELAPSED_TIME_SINCE_LAST_CONTACT.getValue(); if (maxElaspedTime < Long.MAX_VALUE) { routeTable.purge(maxElaspedTime); } dht.setRouteTable(routeTable); if (database != null) { dht.setDatabase(database); } } } } catch (Throwable ignored) { LOG.error("Throwable", ignored); } finally { IOUtils.close(in); } } return dht; } @Override public void stop() { if (!isRunning()) { return; } super.stop(); //Notify our ultrapeers that we disconnected sendUpdatedCapabilities(); if (DHTSettings.PERSIST_ACTIVE_DHT_ROUTETABLE.getValue()) { ObjectOutputStream out = null; try { out = new ObjectOutputStream( new BufferedOutputStream( new SecureOutputStream( new FileOutputStream(FILE)))); out.writeInt(getRouteTableVersion()); synchronized (dht) { out.writeObject(dht.getRouteTable()); Database database = null; if (DHTSettings.PERSIST_DHT_DATABASE.getValue()) { database = dht.getDatabase(); } out.writeObject(database); } out.flush(); } catch (IOException ignored) { } finally { IOUtils.close(out); } } } @Override public void handleConnectionLifecycleEvent(ConnectionLifecycleEvent evt) { //handle connection specific events Connection c = evt.getConnection(); if(c == null) { return; } String host = c.getAddress(); int port = c.getPort(); if(evt.isConnectionCapabilitiesEvent()){ if(c.getConnectionCapabilities().remostHostIsPassiveDHTNode() > -1) { if(LOG.isDebugEnabled()) { LOG.debug("Connection is passive dht node: "+ c); } addPassiveDHTNode(new InetSocketAddress(host, port)); } else if(c.getConnectionCapabilities().remostHostIsActiveDHTNode() > -1) { if(DHTSettings.EXCLUDE_ULTRAPEERS.getValue()) { return; } addActiveDHTNode(new InetSocketAddress(host, port)); } } } @Override public List<IpPort> getActiveDHTNodes(int maxNodes) { if(!isRunning() || !dht.isBootstrapped()) { return Collections.emptyList(); } //return our MRS nodes. We should be first in the list return getMRSNodes(maxNodes, false); } }