com.limegroup.gnutella
Class ManagedConnection

java.lang.Object
  extended bycom.limegroup.gnutella.Connection
      extended bycom.limegroup.gnutella.ManagedConnection
All Implemented Interfaces:
PushProxyInterface, ReplyHandler

public class ManagedConnection
extends Connection
implements ReplyHandler, PushProxyInterface

A Connection managed by a ConnectionManager. Includes a loopForMessages method that runs forever (or until an IOException occurs), receiving and replying to Gnutella messages. ManagedConnection is only instantiated through a ConnectionManager.

ManagedConnection provides a sophisticated message buffering mechanism. When you call send(Message), the message is not actually delivered to the socket; instead it buffered in an application-level buffer. Periodically, a thread reads messages from the buffer, writes them to the network, and flushes the socket buffers. This means that there is no need to manually call flush(). Furthermore, ManagedConnection provides a simple form of flow control. If messages are queued faster than they can be written to the network, they are dropped in the following order: PingRequest, PingReply, QueryRequest, QueryReply, and PushRequest. See the implementation notes below for more details.

All ManagedConnection's have two underlying spam filters: a personal filter (controls what I see) and a route filter (also controls what I pass along to others). See SpamFilter for a description. These filters are configured by the properties in the SettingsManager, but you can change them with setPersonalFilter and setRouteFilter.

ManagedConnection maintain a large number of statistics, such as the current bandwidth for upstream & downstream. ManagedConnection doesn't quite fit the BandwidthTracker interface, unfortunately. On the query-routing3-branch and pong-caching CVS branches, these statistics have been bundled into a single object, reducing the complexity of ManagedConnection.

ManagedConnection also takes care of various VendorMessage handling, in particular Hops Flow, UDP ConnectBack, and TCP ConnectBack. See handleVendorMessage().

This class implements ReplyHandler to route pongs and query replies that originated from it.


Field Summary
 
Fields inherited from class com.limegroup.gnutella.Connection
_200_OK, _closed, _messagesSupported, CONNECT, CONNECTION_CLOSED, CRLF, GNUTELLA_06, GNUTELLA_06_200, GNUTELLA_OK_06, MAX_HANDSHAKE_ATTEMPTS, USER_INPUT_WAIT_TIME
 
Constructor Summary
ManagedConnection(java.lang.String host, int port)
          Creates a new outgoing connection to the specified host on the specified port.
 
Method Summary
 void addReceived()
          Increments the stat for the number of messages received.
 void addReceivedDropped()
          Increments the number of received messages that have been dropped.
 void buildAndStartQueues()
          Builds queues and starts the OutputRunner.
 void close()
          Closes the Connection's socket and thus the connection itself.
 void countDroppedMessage()
          A callback for the ConnectionManager to inform this connection that a message was dropped.
 void flush()
          Does nothing.
 java.util.Set getDomains()
          Returns the domain to which this connection is authenticated
 float getMeasuredDownstreamBandwidth()
          Returns the downstream bandwidth between the last two calls to measureBandwidth.
 float getMeasuredUpstreamBandwidth()
          Returns the upstream bandwidth between the last two calls to measureBandwidth.
 long getNextQRPForwardTime()
          Returns the system time that we should next forward a query route table along this connection.
 int getNumMessagesReceived()
          Returns the number of messages received on this connection
 int getNumMessagesSent()
          Returns the number of messages sent on this connection
 long getNumReceivedMessagesDropped()
          The number of messages received on this connection either filtered out or dropped because we didn't know how to route them.
 int getNumSentMessagesDropped()
          Returns the number of messages I dropped while trying to send on this connection.
protected  java.io.OutputStream getOutputStream()
          Throttles the super's OutputStream.
 float getPercentReceivedDropped()
           
 float getPercentSentDropped()
           
 java.net.InetAddress getPushProxyAddress()
           
 int getPushProxyPort()
           
 java.lang.Object getQRPLock()
           
 double getQueryRouteTablePercentFull()
          Accessor for the last QueryRouteTable's percent full.
 QueryRouteTable getQueryRouteTableReceived()
          Accessor for the QueryRouteTable received along this connection.
 QueryRouteTable getQueryRouteTableSent()
          Accessor for the query route table associated with this.
 void handlePingReply(PingReply pingReply, ReplyHandler receivingConnection)
          This method is called when a reply is received for a PingRequest originating on this Connection.
 void handlePushRequest(PushRequest pushRequest, ReplyHandler receivingConnection)
          This method is called when a PushRequest is received for a QueryReply originating on this Connection.
 void handleQueryReply(QueryReply queryReply, ReplyHandler receivingConnection)
          This method is called when a reply is received for a QueryRequest originating on this Connection.
protected  void handleVendorMessage(VendorMessage vm)
          Call this method when you want to handle us to handle a VM.
 boolean hitsQueryRouteTable(QueryRequest query)
          Determines whether or not the specified QueryRequest instance has a hit in the query routing tables.
 void incrementNextQRPForwardTime(long curTime)
          Increments the next time we should forward query route tables for this connection.
 void initialize()
          Initializes this without timeout; exactly like initialize(0).
 boolean isKillable()
          Returns true if this should not be policed by the ConnectionWatchdog, e.g., because this is a connection to a Clip2 reflector.
 boolean isPersonalSpam(Message m)
          A callback for Message Handler implementations to check to see if a message is considered to be undesirable by the message's receiving connection.
 boolean isSpam(Message m)
          Utility method for checking whether or not this message is considered spam.
 void measureBandwidth()
          Takes a snapshot of the upstream and downstream bandwidth since the last call to measureBandwidth.
 void originateQuery(QueryRequest query)
          This is a specialized send method for queries that we originate, either from ourselves directly, or on behalf of one of our leaves when we're an Ultrapeer.
 void patchQueryRouteTable(PatchTableMessage ptm)
          Patches the QueryRouteTable for this connection.
 Message receive()
          Override of receive to do ConnectionManager stats and to properly shut down the connection on IOException
 Message receive(int timeout)
          Override of receive to do MessageRouter stats and to properly shut down the connection on IOException
 void resetQueryRouteTable(ResetTableMessage rtm)
          Resets the query route table for this connection.
 void send(Message m)
          Sends a message.
 void setHorizonEnabled(boolean enable)
           
 void setPersonalFilter(SpamFilter filter)
           
 void setQueryRouteTableSent(QueryRouteTable qrt)
          Mutator for the last query route table that was sent along this connection.
 void setRouteFilter(SpamFilter filter)
           
 void updateHorizonStats(PingReply pingReply)
          This method is called when a reply is received by this connection for a PingRequest that originated from LimeWire.
 
Methods inherited from class com.limegroup.gnutella.Connection
allowNewPings, allowNewPongs, getBytesReceived, getBytesSent, getConnectionTime, getDomainsAuthenticated, getInetAddress, getInputStream, getIPString, getListeningPort, getNumIntraUltrapeerConnections, getPropertyWritten, getReadSavedFromCompression, getSentSavedFromCompression, getSocket, getSoftMax, getUncompressedBytesReceived, getUncompressedBytesSent, getUserAgent, getVersion, headers, initialize, isClientSupernodeConnection, isConnectBackCapable, isGoodLeaf, isGoodUltrapeer, isGUESSCapable, isGUESSUltrapeer, isHighDegreeConnection, isInitialized, isLeafConnection, isLocal, isOpen, isOutgoing, isReadDeflated, isStable, isStable, isSupernodeClientConnection, isSupernodeConnection, isSupernodeSupernodeConnection, isTempConnection, isUltrapeerQueryRoutingConnection, isWriteDeflated, postInit, receivedHeaders, remoteHostSupportsHopsFlow, remoteHostSupportsPushProxy, remoteHostSupportsTCPConnectBack, remoteHostSupportsUDPConnectBack, supportsGGEP, supportsPongCaching, supportsProbeQueries, supportsVendorMessage, toString
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 
Methods inherited from interface com.limegroup.gnutella.ReplyHandler
allowNewPings, getInetAddress, isGoodLeaf, isGoodUltrapeer, isHighDegreeConnection, isLeafConnection, isOpen, isOutgoing, isStable, isSupernodeClientConnection, isUltrapeerQueryRoutingConnection, supportsPongCaching
 

Constructor Detail

ManagedConnection

public ManagedConnection(java.lang.String host,
                         int port)
Creates a new outgoing connection to the specified host on the specified port.

Parameters:
host - the address of the host we're connecting to
port - the port the host is listening on
Method Detail

initialize

public void initialize()
                throws java.io.IOException,
                       NoGnutellaOkException,
                       BadHandshakeException
Description copied from class: Connection
Initializes this without timeout; exactly like initialize(0).

Overrides:
initialize in class Connection
Throws:
java.io.IOException
NoGnutellaOkException
BadHandshakeException
See Also:
Connection.initialize(int)

resetQueryRouteTable

public void resetQueryRouteTable(ResetTableMessage rtm)
Resets the query route table for this connection. The new table will be of the size specified in rtm and will contain no data. If there is no QueryRouteTable yet created for this connection, this method will create one.

Parameters:
rtm - the ResetTableMessage

patchQueryRouteTable

public void patchQueryRouteTable(PatchTableMessage ptm)
Patches the QueryRouteTable for this connection.

Parameters:
ptm - the patch with the data to update

hitsQueryRouteTable

public boolean hitsQueryRouteTable(QueryRequest query)
Determines whether or not the specified QueryRequest instance has a hit in the query routing tables. If this connection has not yet sent a query route table, this returns false.

Parameters:
query - the QueryRequest to check against the tables
Returns:
true if the QueryRequest has a hit in the tables, otherwise false

getQueryRouteTableReceived

public QueryRouteTable getQueryRouteTableReceived()
Accessor for the QueryRouteTable received along this connection. Can be null if no query routing table has been received yet.

Returns:
the last QueryRouteTable received along this connection

getQueryRouteTablePercentFull

public double getQueryRouteTablePercentFull()
Accessor for the last QueryRouteTable's percent full.


getOutputStream

protected java.io.OutputStream getOutputStream()
                                        throws java.io.IOException
Throttles the super's OutputStream. This works quite well with compressed streams, because the chaining mechanism writes the compressed bytes, ensuring that we do not attempt to request more data (and thus sleep while throttling) than we will actually write.

Overrides:
getOutputStream in class Connection
Throws:
java.io.IOException

receive

public Message receive()
                throws java.io.IOException,
                       BadPacketException
Override of receive to do ConnectionManager stats and to properly shut down the connection on IOException

Overrides:
receive in class Connection
Throws:
java.io.IOException
BadPacketException

receive

public Message receive(int timeout)
                throws java.io.IOException,
                       BadPacketException,
                       java.io.InterruptedIOException
Override of receive to do MessageRouter stats and to properly shut down the connection on IOException

Overrides:
receive in class Connection
Throws:
java.io.IOException
BadPacketException
java.io.InterruptedIOException

send

public void send(Message m)
Sends a message. This overrides does extra buffering so that Messages are dropped if the socket gets backed up. Will remove any extended payloads if the receiving connection does not support GGGEP. Also updates MessageRouter stats.

This method IS thread safe. Multiple threads can be in a send call at the same time for a given connection.

Overrides:
send in class Connection

originateQuery

public void originateQuery(QueryRequest query)
This is a specialized send method for queries that we originate, either from ourselves directly, or on behalf of one of our leaves when we're an Ultrapeer. These queries have a special sending queue of their own and are treated with a higher priority.

Parameters:
query - the QueryRequest to send

addReceivedDropped

public void addReceivedDropped()
Increments the number of received messages that have been dropped.


addReceived

public void addReceived()
Increments the stat for the number of messages received.


flush

public void flush()
           throws java.io.IOException
Does nothing. Since this automatically takes care of flushing output buffers, there is nothing to do. Note that flush() does NOT block for TCP buffers to be emptied.

Overrides:
flush in class Connection
Throws:
java.io.IOException

buildAndStartQueues

public void buildAndStartQueues()
Builds queues and starts the OutputRunner. This is intentionally not in initialize(), as we do not want to create the queues and start the OutputRunner for reject connections.


close

public void close()
Description copied from class: Connection
Closes the Connection's socket and thus the connection itself.

Overrides:
close in class Connection

isSpam

public boolean isSpam(Message m)
Utility method for checking whether or not this message is considered spam.

Parameters:
m - the Message to check
Returns:
true if this is considered spam, otherwise false

countDroppedMessage

public void countDroppedMessage()
A callback for the ConnectionManager to inform this connection that a message was dropped. This happens when a reply received from this connection has no routing path.

Specified by:
countDroppedMessage in interface ReplyHandler

isPersonalSpam

public boolean isPersonalSpam(Message m)
A callback for Message Handler implementations to check to see if a message is considered to be undesirable by the message's receiving connection. Messages ignored for this reason are not considered to be dropped, so no statistics are incremented here.

Specified by:
isPersonalSpam in interface ReplyHandler
Returns:
true if the message is spam, false if it's okay

setRouteFilter

public void setRouteFilter(SpamFilter filter)

setPersonalFilter

public void setPersonalFilter(SpamFilter filter)

getDomains

public java.util.Set getDomains()
Returns the domain to which this connection is authenticated

Specified by:
getDomains in interface ReplyHandler
Returns:
the set (of String) of domains to which this connection is authenticated. Returns null, in case of unauthenticated connection

handlePingReply

public void handlePingReply(PingReply pingReply,
                            ReplyHandler receivingConnection)
This method is called when a reply is received for a PingRequest originating on this Connection. So, just send it back. If modifying this method, note that receivingConnection may by null.

Specified by:
handlePingReply in interface ReplyHandler

handleQueryReply

public void handleQueryReply(QueryReply queryReply,
                             ReplyHandler receivingConnection)
This method is called when a reply is received for a QueryRequest originating on this Connection. So, send it back. If modifying this method, note that receivingConnection may by null.

Specified by:
handleQueryReply in interface ReplyHandler

handlePushRequest

public void handlePushRequest(PushRequest pushRequest,
                              ReplyHandler receivingConnection)
This method is called when a PushRequest is received for a QueryReply originating on this Connection. So, just send it back. If modifying this method, note that receivingConnection may by null.

Specified by:
handlePushRequest in interface ReplyHandler

handleVendorMessage

protected void handleVendorMessage(VendorMessage vm)
Description copied from class: Connection
Call this method when you want to handle us to handle a VM. We may....

Overrides:
handleVendorMessage in class Connection

getNumMessagesSent

public int getNumMessagesSent()
Returns the number of messages sent on this connection


getNumMessagesReceived

public int getNumMessagesReceived()
Returns the number of messages received on this connection

Specified by:
getNumMessagesReceived in interface ReplyHandler

getNumSentMessagesDropped

public int getNumSentMessagesDropped()
Returns the number of messages I dropped while trying to send on this connection. This happens when the remote host cannot keep up with me.


getNumReceivedMessagesDropped

public long getNumReceivedMessagesDropped()
The number of messages received on this connection either filtered out or dropped because we didn't know how to route them.


getPercentReceivedDropped

public float getPercentReceivedDropped()

getPercentSentDropped

public float getPercentSentDropped()

measureBandwidth

public void measureBandwidth()
Takes a snapshot of the upstream and downstream bandwidth since the last call to measureBandwidth.

See Also:
BandwidthTracker.measureBandwidth()

getMeasuredUpstreamBandwidth

public float getMeasuredUpstreamBandwidth()
Returns the upstream bandwidth between the last two calls to measureBandwidth.

See Also:
BandwidthTracker.measureBandwidth()

getMeasuredDownstreamBandwidth

public float getMeasuredDownstreamBandwidth()
Returns the downstream bandwidth between the last two calls to measureBandwidth.

See Also:
BandwidthTracker.measureBandwidth()

setHorizonEnabled

public void setHorizonEnabled(boolean enable)

updateHorizonStats

public void updateHorizonStats(PingReply pingReply)
This method is called when a reply is received by this connection for a PingRequest that originated from LimeWire.


getNextQRPForwardTime

public long getNextQRPForwardTime()
Returns the system time that we should next forward a query route table along this connection. Only valid if isClientSupernodeConnection() is true.


incrementNextQRPForwardTime

public void incrementNextQRPForwardTime(long curTime)
Increments the next time we should forward query route tables for this connection. This depends on whether or not this is a connection to a leaf or to an Ultrapeer.

Parameters:
curTime - the current time in milliseconds, used to calculate the next update time

isKillable

public boolean isKillable()
Returns true if this should not be policed by the ConnectionWatchdog, e.g., because this is a connection to a Clip2 reflector. Default value: true.

Specified by:
isKillable in interface ReplyHandler
Returns:
true if the handler is 'killable', i.e. a clip2 indexing query, otherwise false

getQueryRouteTableSent

public QueryRouteTable getQueryRouteTableSent()
Accessor for the query route table associated with this. This is guaranteed to be non-null, but it may not yet contain any data.

Returns:
the QueryRouteTable instance containing query route table data sent along this connection, or null if no data has yet been sent

setQueryRouteTableSent

public void setQueryRouteTableSent(QueryRouteTable qrt)
Mutator for the last query route table that was sent along this connection.

Parameters:
qrt - the last query route table that was sent along this connection

getPushProxyPort

public int getPushProxyPort()
Specified by:
getPushProxyPort in interface PushProxyInterface
Returns:
a non-negative integer representing the proxy's port for HTTP communication, a negative number if PushProxy isn't supported.

getPushProxyAddress

public java.net.InetAddress getPushProxyAddress()
Specified by:
getPushProxyAddress in interface PushProxyInterface
Returns:
the InetAddress of the remote host - only meaningful if getPushProxyPort() > -1
See Also:
getPushProxyPort()

getQRPLock

public java.lang.Object getQRPLock()