/*
* Mojito Distributed Hash Table (Mojito DHT)
* Copyright (C) 2006-2007 LimeWire LLC
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.limewire.mojito.routing;
import java.io.Serializable;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.List;
import org.limewire.mojito.KUID;
import org.limewire.mojito.concurrent.DHTExecutorService;
import org.limewire.mojito.concurrent.DHTFutureAdapter;
import org.limewire.mojito.result.PingResult;
/**
* Defines the interface for a <a href="http://en.wikipedia.org/wiki/Routing_table">
* RouteTable</a>.
*/
public interface RouteTable extends Serializable {
/**
* Selection mode for the RouteTable's select() operation.
*/
public static enum SelectMode {
/**
* Selects all Contacts.
*/
ALL,
/**
* Selects only alive Contacts.
*/
ALIVE,
/**
* Selects only alive and the local Contacts.
*/
ALIVE_WITH_LOCAL;
}
/**
* Rebuild mode for the RouteTable's purge() operation.
*/
public static enum PurgeMode {
/**
* Drops all Contacts in the replacement cache.
*/
DROP_CACHE,
/**
* Deletes all non-alive Contacts from the active
* RouteTable and fill up the new slots with alive
* Contacts from the replacement cache.
*/
PURGE_CONTACTS,
/**
* Merges all Buckets by essentially rebuilding the
* entire RouteTable.
*/
MERGE_BUCKETS,
/**
* Marks all Contacts as unknown.
*/
STATE_TO_UNKNOWN,
}
/**
* Adds a new Contact or if it's already in the RouteTable updates
* its contact information.
*
* @param node the Contact we would like to add
*/
public void add(Contact node);
/**
* Returns a Contact from the local RoutingTable if such Contact exists
* and null if it doesn't.
*/
public Contact get(KUID nodeId);
/**
* Selects the best matching Contact for the provided KUID.
* This method will guaranteed return a non-null value if the
* RoutingTable is not empty.
*/
public Contact select(KUID nodeId);
/**
* Selects the best matching k Contacts for the provided KUID. The returned
* Contacts are sorted by their closeness to the lookup Key from closest to
* least closest Contact. Use {@link org.limewire.mojito.util.BucketUtils#sort(List)}
* to sort the list from least-recently-seen to most-recently-seen Contact.
*
* @param nodeId the lookup KUID
* @param count the number of Contact (maybe less if RoutingTable has less than 'count' entries!)
* @param mode whether or not only alive Contacts should be in the result set
* @return list of Contacts sorted by closeness
*/
public Collection<Contact> select(KUID nodeId, int count, SelectMode mode);
/**
* Notifies the RoutingTable that the Contact with the provided
* KUID has failed to answer to a request.
*/
public void handleFailure(KUID nodeId, SocketAddress address);
/**
* Returns all Contacts as List.
*/
public Collection<Contact> getContacts();
/**
* Returns Contacts that are actively used for routing.
*/
public Collection<Contact> getActiveContacts();
/**
* Returns cached Contacts that are in the replacement cache.
*/
public Collection<Contact> getCachedContacts();
/**
* Returns a Bucket that is nearest (XOR distance)
* to the given KUID.
*/
public Bucket getBucket(KUID nodeId);
/**
* Returns all Buckets as an Collection.
*/
public Collection<Bucket> getBuckets();
/**
* Returns a List of KUIDs that need to be looked up in order
* to refresh (or bootstrap) the RouteTable.
*
* @param bootstrapping whether or not this refresh is done during bootstrap
*/
public Collection<KUID> getRefreshIDs(boolean bootstrapping);
/**
* Clears all elements from the RoutingTable.
*/
public void clear();
/**
* Purges all Contacts from the RouteTable whose time stamp
* is older than the given time and merges all Buckets.
*/
public void purge(long elapsedTimeSinceLastContact);
/**
* Purges/Rebuilds the RouteTable based on the given Modes.
*/
public void purge(PurgeMode first, PurgeMode... rest);
/**
* Returns the number of live and cached Contacts in the Route Table.
*/
public int size();
/**
* Returns whether or not the given Contact is the local
* Node.
*/
public boolean isLocalNode(Contact node);
/**
* Returns the local Node.
*/
public Contact getLocalNode();
/**
* Sets the RouteTable PingCallback.
*/
public void setContactPinger(ContactPinger pinger);
/**
* Sets the executor on which to notify for route table events.
*/
public void setNotifier(DHTExecutorService executor);
/**
* Adds a RouteTableListener.
*
* @param l the RouteTableListener to add
*/
public void addRouteTableListener(RouteTableListener l);
/**
* Removes a RouteTableListener.
*
* @param l the RouteTableListener to remove
*/
public void removeRouteTableListener(RouteTableListener l);
/**
* An interface used by the RouteTable to access
* external resources.
*/
public static interface ContactPinger {
/** Sends a PING to the given Node */
public void ping(Contact node, DHTFutureAdapter<PingResult> listener);
}
/**
* The interface to receive RouteTable events.
*/
public static interface RouteTableListener {
/**
* Invoked when an event occurs.
*
* @param event the event that occurred
*/
public void handleRouteTableEvent(RouteTableEvent event);
}
/**
* Created and fired for various RouteTable events.
*/
public static class RouteTableEvent {
/**
* The types of events that may occur.
*/
public static enum EventType {
/** A Contact was added to the RouteTable. */
ADD_ACTIVE_CONTACT,
/** A Contact was added to the replacement cache. */
ADD_CACHED_CONTACT,
/** A Contact was replaced. */
REPLACE_CONTACT,
/** A Contact was updated. */
UPDATE_CONTACT,
/** A Contact was removed. */
REMOVE_CONTACT,
/** A Contact was contacted to check if it's alive. */
CONTACT_CHECK,
/** A Bucket was split. */
SPLIT_BUCKET,
/** The RouteTable was cleared.*/
CLEAR;
}
private final RouteTable routeTable;
private final Bucket bucket;
private final Bucket left;
private final Bucket right;
private final Contact existing;
private final Contact node;
private final EventType type;
private final long timeStamp = System.currentTimeMillis();
public RouteTableEvent(RouteTable routeTable,
Bucket bucket, Bucket left, Bucket right,
Contact existing, Contact node, EventType type) {
this.routeTable = routeTable;
this.bucket = bucket;
this.left = left;
this.right = right;
this.existing = existing;
this.node = node;
this.type = type;
}
/**
* Returns the RouteTable which triggered the Event.
*/
public RouteTable getRouteTable() {
return routeTable;
}
/**
* The Bucket where an Event occurred.
*/
public Bucket getBucket() {
return bucket;
}
/**
* Returns the new left hand Bucket if this is
* a SPLIT_BUCKET event and null otherwise.
*/
public Bucket getLeftBucket() {
return left;
}
/**
* Returns the new right hand Bucket if this is
* a SPLIT_BUCKET event and null otherwise.
*/
public Bucket getRightBucket() {
return right;
}
/**
* Returns the existing Contact that was updated.
* This might be null in certain cases!
*/
public Contact getExistingContact() {
return existing;
}
/**
* Returns the Contact that was added to the RouteTable.
*/
public Contact getContact() {
return node;
}
/**
* Returns the type of the Event.
*/
public EventType getEventType() {
return type;
}
/**
* Returns the time when this Event occurred.
*/
public long getTimeStamp() {
return timeStamp;
}
}
}