package com.limegroup.gnutella.udpconnect;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Manage the assignment of connectionIDs and the routing of
* UDPConnectionMessages.
*/
public class UDPMultiplexor {
private static final Log LOG =
LogFactory.getLog(UDPMultiplexor.class);
/** Keep track of a singleton instance */
private static UDPMultiplexor _instance = new UDPMultiplexor();
/** The 0 slot is for incoming new connections so it is not assigned */
public static final byte UNASSIGNED_SLOT = 0;
/** Keep track of the assigned connections */
private volatile WeakReference[] _connections;
/** Keep track of the last assigned connection id so that we can use a
circular assignment algorithm. This should cut down on message
collisions after the connection is shut down. */
private int _lastConnectionID;
/**
* Return the UDPMultiplexor singleton.
*/
public static UDPMultiplexor instance() {
return _instance;
}
/**
* Initialize the UDPMultiplexor.
*/
private UDPMultiplexor() {
_connections = new WeakReference[256];
_lastConnectionID = 0;
}
/**
* Determines if we're connected to the given host.
*/
public boolean isConnectedTo(InetAddress host) {
WeakReference[] array = _connections;
if(_lastConnectionID == 0)
return false;
for(int i = 0; i < array.length; i++) {
WeakReference conRef = array[i];
if(conRef != null) {
UDPConnectionProcessor con = (UDPConnectionProcessor)conRef.get();
if(con != null && host.equals(con.getInetAddress())) {
return true;
}
}
}
return false;
}
/**
* Register a UDPConnectionProcessor for receiving incoming events and
* return the assigned connectionID;
*/
public synchronized byte register(UDPConnectionProcessor con) {
int connID;
WeakReference[] copy = new WeakReference[_connections.length];
for (int i= 0 ; i< _connections.length;i++)
copy[i] = _connections[i];
for (int i = 1; i <= copy.length; i++) {
connID = (_lastConnectionID + i) % 256;
// We don't assign zero.
if ( connID == 0 )
continue;
// If the slot is open, take it.
if (copy[connID] == null || copy[connID].get()==null) {
_lastConnectionID = connID;
copy[connID] = new WeakReference(con);
_connections=copy;
return (byte) connID;
}
}
return UNASSIGNED_SLOT;
}
/**
* Unregister a UDPConnectionProcessor for receiving incoming messages.
* Free up the slot.
*/
public synchronized void unregister(UDPConnectionProcessor con) {
int connID = (int) con.getConnectionID() & 0xff;
WeakReference[] copy = new WeakReference[_connections.length];
for (int i= 0 ; i< _connections.length;i++)
copy[i] = _connections[i];
if ( copy[connID]!=null && copy[connID].get() == con ) {
copy[connID].clear();
copy[connID]=null;
}
_connections=copy;
}
/**
* Route a message to the UDPConnectionProcessor identified in the messages
* connectionID;
*/
public void routeMessage(UDPConnectionMessage msg,
InetAddress senderIP, int senderPort) {
UDPConnectionProcessor con;
WeakReference[] array = _connections;
int connID = (int) msg.getConnectionID() & 0xff;
// If connID equals 0 and SynMessage then associate with a connection
// that appears to want it (connecting and with knowledge of it).
if ( connID == 0 && msg instanceof SynMessage ) {
if(LOG.isDebugEnabled()) {
LOG.debug("Receiving SynMessage :"+msg);
}
for (int i = 1; i < array.length; i++) {
if (array[i]==null)
con=null;
else
con = (UDPConnectionProcessor)array[i].get();
if ( con != null &&
con.isConnecting() &&
con.matchAddress(senderIP, senderPort) ) {
if(LOG.isDebugEnabled()) {
LOG.debug("routeMessage to conn:"+i+" Syn:"+msg);
}
con.handleMessage(msg);
break;
}
}
// Note: eventually these messages should find a match
// so it is safe to throw away premature ones
} else { // If valid connID then send on to connection
if (array[connID]==null)
con = null;
else
con = (UDPConnectionProcessor)array[connID].get();
if ( con != null && con.matchAddress(senderIP, senderPort) ) {
con.handleMessage(msg);
}
}
}
}