package org.limewire.nio.statemachine;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.limewire.nio.NIODispatcher;
import org.limewire.nio.observer.Shutdownable;
import org.limewire.util.FileUtils;
/**
* Blocks until the state machine finishes; use <code>BlockingStateMachine</code>
* as an alternative to {@link IOStateMachine}.
* <p>
* <code>BlockingStateMachine</code> is useful if you want to use the same state
* code from an NIO portion where only <code>OutputStreams</code> and
* <code>InputStreams</code> are available.
*/
public class BlockingStateMachine implements Closeable, Shutdownable {
private static final Log LOG = LogFactory.getLog(BlockingStateMachine.class);
/** The states this will use while handshaking.*/
private final List<IOState> states;
/** A reading channel wrapping an InputStream. */
private final ReadableByteChannel readChannel;
/** A writing channel wrapping an OutputStream. */
private final WritableByteChannel writeChannel;
/** The ByteBuffer to use for reading. */
private final ByteBuffer readBuffer;
public BlockingStateMachine(List<IOState> states, InputStream in, OutputStream out) {
this(states, 2048, in, out);
}
public BlockingStateMachine(List<IOState> states, int bufferSize, InputStream in, OutputStream out) {
this.states = states;
this.readBuffer = NIODispatcher.instance().getBufferCache().getHeap(bufferSize);
this.readChannel = Channels.newChannel(in);
this.writeChannel = Channels.newChannel(out);
}
/**
* Adds a new state to process.
*/
public void addState(final IOState newState) {
if(LOG.isDebugEnabled())
LOG.debug("Adding single state: " + newState);
states.add(newState);
}
/** Adds a collection of new states to process. */
public void addStates(final List<? extends IOState> newStates) {
if(LOG.isDebugEnabled())
LOG.debug("Adding multiple states: " + newStates);
states.addAll(newStates);
}
/** Adds an array of new states to process. */
public void addStates(final IOState... newStates) {
if(LOG.isDebugEnabled())
LOG.debug("Adding multiple states...");
for(int i = 0; i < newStates.length; i++) {
if(LOG.isDebugEnabled())
LOG.debug(" state[" + i + "]: " + newStates[i]);
states.add(newStates[i]);
}
}
/** Aborts this statemachine. */
public void close() {
FileUtils.close(readChannel);
FileUtils.close(writeChannel);
}
/** Releases any data the statemachine retained. */
public void shutdown() {
NIODispatcher.instance().getBufferCache().release(readBuffer);
}
/** Begins processing through the states. Returns only when every state is processed. */
public void process() throws IOException {
for(Iterator<IOState> i = states.iterator(); i.hasNext(); ) {
IOState current = i.next();
if(current.isReading())
current.process(readChannel, readBuffer);
else if(current.isWriting())
current.process(writeChannel, null);
else
throw new IllegalStateException("expected reading | writing state");
i.remove();
}
}
}