package com.limegroup.gnutella.dime; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.Iterator; import java.util.List; import org.limewire.io.ByteBufferOutputStream; import org.limewire.nio.statemachine.WriteState; import com.limegroup.gnutella.connection.MessageWriter; /** * Uses NIO to write a list of {@link DIMERecord} objects to a channel. Based on * {@link MessageWriter}. */ public class AsyncDimeWriter extends WriteState { private final Iterator<DIMERecord> recordIt; /** * The OutputStream that messages are written to. For efficieny, the stream * internally uses a ByteBuffer and we get the buffer directly to write to * our sink channel. This prevents recreation of many byte[]s. */ private final ByteBufferOutputStream out; public boolean isFirstRecord = true; private long amountProcessed; private boolean flipped; public AsyncDimeWriter(List<DIMERecord> records) { this.recordIt = records.iterator(); this.out = new ByteBufferOutputStream(); } @Override protected boolean processWrite(WritableByteChannel channel, ByteBuffer buffer) throws IOException { // first try to write any leftover data. if(writeRemaining(channel)) //still have data to send. return true; boolean isLastRecord = !recordIt.hasNext(); while (!isLastRecord) { DIMERecord current = recordIt.next(); if (isFirstRecord) { current.setFirstRecord(true); isFirstRecord = false; } else { current.setFirstRecord(false); } isLastRecord = !recordIt.hasNext(); current.setLastRecord(isLastRecord); current.write(out); if (writeRemaining(channel)) // still have data to send. return true; } return false; } public long getAmountProcessed() { return amountProcessed; } /** * Writes any data that was left in the buffer. As an optimization, * we do not recompact the buffer if more data can be written. Instead, * we just wait till we can completely write the buffer & then clear it * entirely. This prevents the need to compact the buffer. * @param channel */ private boolean writeRemaining(WritableByteChannel channel) throws IOException { // if there was data left in the stream, try writing it. ByteBuffer buffer = out.getBuffer(); // write any data that was leftover in the buffer. if (flipped || buffer.position() > 0) { // prepare for writing... if(!flipped) { buffer.flip(); flipped = true; } // write. int written = channel.write(buffer); amountProcessed += written; // if we couldn't write everything, exit. if(buffer.hasRemaining()) return true; // still have data to write. flipped = false; buffer.clear(); } return false; // wrote everything. } }