package edu.washington.cs.oneswarm.f2f.network;
import java.util.TimerTask;
import java.util.logging.Logger;
import org.gudy.azureus2.core3.util.Average;
import org.gudy.azureus2.core3.util.DirectByteBuffer;
import com.aelitis.azureus.core.networkmanager.NetworkManager;
import com.aelitis.azureus.core.peermanager.messaging.MessageException;
import edu.washington.cs.oneswarm.f2f.Friend;
import edu.washington.cs.oneswarm.f2f.messaging.OSF2FChannelDataMsg;
import edu.washington.cs.oneswarm.f2f.messaging.OSF2FChannelReset;
import edu.washington.cs.oneswarm.f2f.messaging.OSF2FHashSearch;
import edu.washington.cs.oneswarm.f2f.messaging.OSF2FHashSearchResp;
import edu.washington.cs.oneswarm.f2f.messaging.OSF2FMessage;
import edu.washington.cs.oneswarm.f2f.network.DelayedExecutorService.DelayedExecutor;
import edu.washington.cs.oneswarm.f2f.servicesharing.OSF2FServiceDataMsg;
/**
* This class handles shared functionality of all overlay end-points and must be
* extended.
*
* @author isdal
*
*/
public abstract class OverlayEndpoint implements EndpointInterface {
private final static Logger logger = Logger.getLogger(OverlayEndpoint.class.getName());
/*
* max number of ms that a message can be delivered earlier than
* overlayDelayMs if that avoids a call to Thread.sleep()
*/
private final static int INCOMING_MESSAGE_DELAY_SLACK = 10;
private long bytesIn = 0;
protected long bytesOut = 0;
protected boolean started = false;
protected final int channelId;
protected boolean closed = false;
protected String closeReason = "";
private String desc = null;
private final DelayedExecutor delayedOverlayMessageTimer;
protected Average downloadRateAverage = Average.getInstance(1000, 10);
protected final FriendConnection friendConnection;
protected long lastMsgTime;
private final long overlayDelayMs;
protected final int pathID;
private boolean sentReset = false;
private final long startTime;
private final int TIMEOUT = 2 * 60 * 1000;
protected Average uploadRateAverage = Average.getInstance(1000, 10);
private final OSF2FHashSearch search;
private final OSF2FHashSearchResp response;
protected final boolean outgoing;
public OverlayEndpoint(FriendConnection friendConnection, int pathID, long overlayDelayMs,
OSF2FHashSearch search, OSF2FHashSearchResp response, boolean outgoing) {
this.friendConnection = friendConnection;
this.channelId = response.getChannelID();
this.pathID = pathID;
this.overlayDelayMs = overlayDelayMs;
this.lastMsgTime = System.currentTimeMillis();
this.startTime = System.currentTimeMillis();
delayedOverlayMessageTimer = DelayedExecutorService.getInstance().getFixedDelayExecutor(
overlayDelayMs);
this.search = search;
this.response = response;
this.outgoing = outgoing;
}
protected abstract void cleanup();
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#isOutgoing()
*/
@Override
public boolean isOutgoing() {
return outgoing;
}
private void deregister() {
// remove it from the friend connection
friendConnection.deregisterOverlayTransport(this);
cleanup();
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#close(java.lang
* .String)
*/
@Override
public void close(String reason) {
if (!closed) {
closeReason = "peer - " + reason;
logger.fine(getDescription() + ": OverlayTransport closed, reason:" + closeReason);
closed = true;
this.sendReset();
}
// we don't expect anyone to read whatever we have left in the buffer
this.destroyBufferedMessages();
deregister();
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#closeChannelReset
* ()
*/
@Override
public void closeChannelReset() {
if (sentReset) {
// ok, this is the response to our previous close
deregister();
} else {
if (!closed) {
closeReason = "remote host closed overlay channel";
logger.fine(getDescription() + ": OverlayTransport closed, reason:" + closeReason);
// this is the remote side saying that the connection is closed
// send a reset back to confirm
closed = true;
sendReset();
deregister();
}
}
}
/*
* (non-Javadoc)
*
* @see edu.washington.cs.oneswarm.f2f.network.EndpointInterface#
* closeConnectionClosed(java.lang.String)
*/
@Override
public void closeConnectionClosed(FriendConnection f, String reason) {
closeReason = reason;
logger.fine(getDescription() + ": OverlayTransport closed, reason:" + closeReason);
closed = true;
deregister();
}
protected abstract void destroyBufferedMessages();
/*
* (non-Javadoc)
*
* @see edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getAge()
*/
@Override
public long getAge() {
return System.currentTimeMillis() - startTime;
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getArtificialDelay
* ()
*/
@Override
public long getArtificialDelay() {
return overlayDelayMs;
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getBytesIn()
*/
@Override
public long getBytesIn() {
return bytesIn;
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getBytesOut()
*/
@Override
public long getBytesOut() {
return bytesOut;
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getChannelId()
*/
@Override
public int getChannelId() {
return channelId;
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getDescription()
*/
@Override
public String getDescription() {
if (desc == null) {
desc = NetworkManager.OSF2F_TRANSPORT_PREFIX + ": "
+ friendConnection.getRemoteFriend().getNick() + ":"
+ Integer.toHexString(channelId);
}
return desc;
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getDownloadRate
* ()
*/
@Override
public int getDownloadRate() {
return (int) downloadRateAverage.getAverage();
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getLastMsgTime()
*/
@Override
public long getLastMsgTime() {
return System.currentTimeMillis() - lastMsgTime;
}
/*
* (non-Javadoc)
*
* @see edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getPathID()
*/
@Override
public int[] getPathID() {
int[] paths = new int[1];
paths[0] = pathID;
return paths;
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getRemoteFriend
* ()
*/
@Override
public Friend getRemoteFriend() {
return friendConnection.getRemoteFriend();
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getRemoteIP()
*/
@Override
public String getRemoteIP() {
return friendConnection.getRemoteIp().getHostAddress();
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#getUploadRate()
*/
@Override
public int getUploadRate() {
return (int) uploadRateAverage.getAverage();
}
protected abstract void handleDelayedOverlayMessage(final OSF2FChannelDataMsg msg);
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#incomingOverlayMsg
* (edu.washington.cs.oneswarm.f2f.messaging.OSF2FChannelDataMsg)
*/
@Override
public void incomingOverlayMsg(OSF2FChannelDataMsg msg) {
lastMsgTime = System.currentTimeMillis();
msg.setByteInChannel(bytesIn);
bytesIn += msg.getMessageSize();
if (closed) {
return;
}
if (isService()) {
try {
if (!(msg instanceof OSF2FServiceDataMsg)) {
msg = OSF2FServiceDataMsg.fromChannelMessage(msg);
}
PacketListener setupPacketListener = friendConnection.getSetupPacketListener();
if (setupPacketListener != null && msg.getByteInChannel() == 0) {
setupPacketListener.packetArrivedAtFinalDestination(friendConnection, search,
response, msg, outgoing);
}
} catch (MessageException m) {
logger.warning("Got non service message to a service endpoint!: " + m.getMessage());
return;
}
}
final OSF2FChannelDataMsg message = msg;
delayedOverlayMessageTimer.queue(overlayDelayMs, INCOMING_MESSAGE_DELAY_SLACK,
new TimerTask() {
@Override
public void run() {
handleDelayedOverlayMessage(message);
}
});
}
protected abstract boolean isService();
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#isLANLocal()
*/
@Override
public boolean isLANLocal() {
return friendConnection.getNetworkConnection().isLANLocal();
}
/*
* (non-Javadoc)
*
* @see edu.washington.cs.oneswarm.f2f.network.EndpointInterface#isStarted()
*/
@Override
public boolean isStarted() {
return started;
}
/*
* (non-Javadoc)
*
* @see
* edu.washington.cs.oneswarm.f2f.network.EndpointInterface#isTimedOut()
*/
@Override
public boolean isTimedOut() {
return System.currentTimeMillis() - lastMsgTime > TIMEOUT;
}
private void sendReset() {
sentReset = true;
friendConnection.sendChannelRst(new OSF2FChannelReset(OSF2FChannelReset.CURRENT_VERSION,
channelId));
}
/*
* (non-Javadoc)
*
* @see edu.washington.cs.oneswarm.f2f.network.EndpointInterface#start()
*/
@Override
public abstract void start();
protected long writeMessageToFriendConnection(DirectByteBuffer msgBuffer) {
OSF2FChannelDataMsg msg = new OSF2FChannelDataMsg(OSF2FMessage.CURRENT_VERSION, channelId,
msgBuffer);
long totalWritten = msgBuffer.remaining(DirectByteBuffer.SS_MSG);
this.writeMessage(msg);
bytesOut += totalWritten;
return totalWritten;
}
protected void writeMessage(OSF2FChannelDataMsg msg) {
msg.setForward(false);
msg.setByteInChannel(bytesOut);
PacketListener setupPacketListener = friendConnection.getSetupPacketListener();
if (setupPacketListener != null && msg.getByteInChannel() == 0) {
setupPacketListener.packetAddedToTransportQueue(friendConnection, search, response,
outgoing, msg);
}
friendConnection.sendChannelMsg(msg, true);
}
}