package edu.washington.cs.oneswarm.f2f.servicesharing; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import org.gudy.azureus2.core3.util.DirectByteBuffer; import org.gudy.azureus2.core3.util.DirectByteBufferPool; import com.aelitis.azureus.core.networkmanager.RawMessage; import com.aelitis.azureus.core.networkmanager.Transport; import com.aelitis.azureus.core.networkmanager.impl.RawMessageImpl; import com.aelitis.azureus.core.peermanager.messaging.Message; import com.aelitis.azureus.core.peermanager.messaging.MessageException; import com.aelitis.azureus.core.peermanager.messaging.MessageStreamDecoder; import com.aelitis.azureus.core.peermanager.messaging.MessageStreamEncoder; import edu.washington.cs.oneswarm.f2f.messaging.OSF2FMessage; public class DataMessage implements OSF2FMessage { private static final byte SS = DirectByteBuffer.SS_MSG; public final static int MAX_SERVICE_PAYLOAD_SIZE = 1024; private DirectByteBuffer buffer = null; private static String ID = "RAW_MESSAGE"; private final String desc; private final int size; public DataMessage(DirectByteBuffer _buffer) { this.buffer = _buffer; size = _buffer.remaining(SS); desc = "Raw message: " + size + " bytes"; } @Override public int getMessageSize() { return size; } @Override public String getID() { return ID; } @Override public byte[] getIDBytes() { return ID.getBytes(); } @Override public String getFeatureID() { return (null); } @Override public int getFeatureSubID() { return (0); } @Override public int getType() { return (TYPE_DATA_PAYLOAD); } @Override public String getDescription() { return desc; } @Override public byte getVersion() { return (1); } public DirectByteBuffer getPayload() { return (buffer); } @Override public DirectByteBuffer[] getData() { return new DirectByteBuffer[] { buffer }; } @Override public Message deserialize(DirectByteBuffer data, byte version) throws MessageException { throw (new MessageException("not implemented")); } @Override public void destroy() { if (buffer != null) { buffer.returnToPool(); buffer = null; } } /** * Retrieve the payload from this message for transfer into a new message. * * The new message is responsible for returning the buffer on destroy. * * @return */ public DirectByteBuffer transferPayload() { DirectByteBuffer data = buffer; buffer = null; return data; } static class RawMessageEncoder implements MessageStreamEncoder { @Override public RawMessage[] encodeMessage(Message base_message) { return new RawMessage[] { new RawMessageImpl(base_message, base_message.getData(), RawMessage.PRIORITY_NORMAL, true, null) }; } } static class RawMessageDecoder implements MessageStreamDecoder { DirectByteBuffer payload_buffer; private boolean paused = false; private IOException pendingException; private final ArrayList<Message> messages_last_read = new ArrayList<Message>(); @Override public void resumeDecoding() { paused = false; } @Override public Message[] removeDecodedMessages() { if (messages_last_read.isEmpty()) { return null; } Message[] msgs = messages_last_read.toArray(new Message[messages_last_read.size()]); messages_last_read.clear(); return msgs; } @Override public int performStreamDecode(Transport transport, int max_bytes) throws IOException { if (pendingException != null) { throw pendingException; } int bytes_left = max_bytes; while (bytes_left > 0) { if (payload_buffer == null) { payload_buffer = DirectByteBufferPool.getBuffer(SS, MAX_SERVICE_PAYLOAD_SIZE); } if (paused) { break; } // If we reach the end of the stream (get and // "end of stream on socket read" error) // return whatever bytes we have read so far and package them up // in a data message // then save the exception and return it on the next read. long read = 0; try { read = transport.read(new ByteBuffer[] { payload_buffer.getBuffer(SS) }, 0, 1); } catch (IOException e) { pendingException = e; } bytes_left -= read; // Message is done if: // * payload is full // * transport has no more data if (payload_buffer.remaining(SS) == 0 || read == 0) { if (payload_buffer.position(SS) > 0) { payload_buffer.flip(SS); Message msg = new DataMessage(payload_buffer); payload_buffer = null; messages_last_read.add(msg); } // If we read all from transport, break if (read == 0) { break; } } } return max_bytes - bytes_left; } @Override public void pauseDecoding() { paused = true; } @Override public int getProtocolBytesDecoded() { return 0; } @Override public int getPercentDoneOfCurrentMessage() { return (int) (getDataBytesDecoded() * 100.0 / MAX_SERVICE_PAYLOAD_SIZE); } @Override public int getDataBytesDecoded() { if (payload_buffer == null) { return 0; } return payload_buffer.position(SS); } @Override public ByteBuffer destroy() { if (payload_buffer != null) { payload_buffer.returnToPool(); payload_buffer = null; } for (Message msg : messages_last_read) { msg.destroy(); } messages_last_read.clear(); return ByteBuffer.allocate(0); } } }