package com.limegroup.gnutella.dht; import java.net.SocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.limewire.mojito.KUID; import org.limewire.mojito.concurrent.DHTExecutorService; import org.limewire.mojito.routing.Bucket; import org.limewire.mojito.routing.ClassfulNetworkCounter; 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.KademliaSettings; import org.limewire.mojito.util.CollectionUtils; import org.limewire.mojito.util.FixedSizeHashMap; class PassiveLeafRouteTable implements RouteTable { private static final long serialVersionUID = 2378400850935282184L; private final Bucket bucket; private final Contact localNode; public PassiveLeafRouteTable(Vendor vendor, Version version) { localNode = ContactFactory.createLocalContact(vendor, version, true); bucket = new BucketImpl(this, KademliaSettings.REPLICATION_PARAMETER.getValue()); } public synchronized void add(Contact node) { if (isLocalNode(node)) { return; } if (node.isFirewalled()) { return; } ClassfulNetworkCounter counter = bucket.getClassfulNetworkCounter(); if (counter == null || counter.isOkayToAdd(node)) { bucket.addActiveContact(node); } } public synchronized void addRouteTableListener(RouteTableListener l) { } public synchronized void removeRouteTableListener(RouteTableListener l) { } public synchronized void clear() { bucket.clear(); } public synchronized Contact get(KUID nodeId) { if (nodeId.equals(localNode.getNodeID())) { return localNode; } return bucket.get(nodeId); } public synchronized Collection<Contact> getActiveContacts() { List<Contact> nodes = new ArrayList<Contact>(bucket.getActiveContacts()); nodes.add(localNode); return nodes; } public synchronized Bucket getBucket(KUID nodeId) { return bucket; } public synchronized Collection<Bucket> getBuckets() { return Collections.singleton(bucket); } public synchronized Collection<Contact> getCachedContacts() { return bucket.getCachedContacts(); } public synchronized Collection<Contact> getContacts() { return bucket.getActiveContacts(); } public synchronized Contact getLocalNode() { return localNode; } public synchronized Collection<KUID> getRefreshIDs(boolean bootstrapping) { return Collections.emptySet(); } public synchronized void handleFailure(KUID nodeId, SocketAddress address) { bucket.remove(nodeId); } public synchronized boolean isLocalNode(Contact node) { return localNode.equals(node); } public synchronized void purge(long elapsedTimeSinceLastContact) { } public synchronized void purge(PurgeMode first, PurgeMode... rest) { } public synchronized Collection<Contact> select(KUID nodeId, int count, SelectMode mode) { Collection<Contact> nodes = bucket.select(nodeId, count); if (nodes.size() >= count || mode == SelectMode.ALIVE) { return nodes; } nodes = new ArrayList<Contact>(nodes); nodes.add(localNode); return nodes; } public synchronized Contact select(KUID nodeId) { Contact c = bucket.select(nodeId); if (c != null) { return c; } return localNode; } public synchronized void setContactPinger(ContactPinger pinger) { } public void setNotifier(DHTExecutorService e){} public synchronized int size() { return bucket.size() + 1; } @Override public synchronized String toString() { StringBuilder buffer = new StringBuilder(); buffer.append(getLocalNode()).append("\n"); buffer.append(bucket); return buffer.toString(); } private static class BucketImpl implements Bucket { private static final long serialVersionUID = 7625655390844705296L; private final RouteTable routeTable; private final int k; private final Map<KUID, Contact> map; private final ClassfulNetworkCounter counter; public BucketImpl(RouteTable routeTable, int k) { this.routeTable = routeTable; this.k = k; counter = new ClassfulNetworkCounter(this); map = new FixedSizeHashMap<KUID, Contact>(k, 0.75f, true, k) { private static final long serialVersionUID = 4026436727356877846L; @Override protected boolean removeEldestEntry(Entry<KUID, Contact> eldest) { if (super.removeEldestEntry(eldest)) { getClassfulNetworkCounter().decrementAndGet(eldest.getValue()); return true; } return false; } }; } public ClassfulNetworkCounter getClassfulNetworkCounter() { return counter; } public boolean isLocalNode(Contact node) { return routeTable.isLocalNode(node); } public void addActiveContact(Contact node) { updateContact(node); } public Contact updateContact(Contact node) { Contact existing = map.remove(node.getNodeID()); if (existing != null) { getClassfulNetworkCounter().decrementAndGet(existing); } Contact previous = map.put(node.getNodeID(), node); assert (previous == null); getClassfulNetworkCounter().incrementAndGet(node); return existing; } public Contact addCachedContact(Contact node) { throw new UnsupportedOperationException(); } public void clear() { map.clear(); } public boolean contains(KUID nodeId) { return map.containsKey(nodeId); } public boolean containsActiveContact(KUID nodeId) { return contains(nodeId); } public boolean containsCachedContact(KUID nodeId) { return false; } public Contact get(KUID nodeId) { return map.get(nodeId); } public Contact getActiveContact(KUID nodeId) { return get(nodeId); } public Collection<Contact> getActiveContacts() { return new ArrayList<Contact>(map.values()); } public Collection<Contact> getCachedContacts() { return Collections.emptySet(); } public int getActiveSize() { return map.size(); } public int getCacheSize() { return getCachedContacts().size(); } public KUID getBucketID() { return KUID.MINIMUM; } public Contact getCachedContact(KUID nodeId) { return null; } public int getDepth() { return 0; } public Contact getLeastRecentlySeenActiveContact() { Contact lrs = null; for (Contact c : map.values()) { lrs = c; } return lrs; } public Contact getLeastRecentlySeenCachedContact() { return null; } public Contact getMostRecentlySeenActiveContact() { for (Contact c : map.values()) { return c; } return null; } public Contact getMostRecentlySeenCachedContact() { return null; } public long getTimeStamp() { return Long.MAX_VALUE; } public boolean isActiveFull() { return false; } public int getMaxActiveSize() { return k; } public boolean isCacheFull() { return false; } public boolean isRefreshRequired() { return false; } public boolean isTooDeep() { return false; } public void purge() { } public boolean remove(KUID nodeId) { Contact node = map.remove(nodeId); if (node != null) { getClassfulNetworkCounter().decrementAndGet(node); return true; } return false; } public boolean removeActiveContact(KUID nodeId) { return remove(nodeId); } public boolean removeCachedContact(KUID nodeId) { return false; } public Collection<Contact> select(KUID nodeId, int count) { Contact[] nodes = new Contact[Math.min(count, map.size())]; int index = 0; for (Contact c : map.values()) { if (index >= nodes.length) { break; } nodes[index++] = c; } return Arrays.asList(nodes); } public Contact select(KUID nodeId) { for (Contact c : map.values()) { return c; } return null; } public int size() { return getActiveSize() + getCacheSize(); } public List<Bucket> split() { throw new UnsupportedOperationException(); } public void touch() { } @Override public String toString() { return CollectionUtils.toString(map.values()); } public boolean isInSmallestSubtree() { return false; } } }