package edu.vanderbilt.cs282.feisele.lab06.service;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class stands in place of the various mechanisms used by Channels
* generally. The network is emulated by queues of ByteBuffer objects.
*/
public class MockNetworkStack extends Socket {
public static final Logger logger = LoggerFactory.getLogger("mock.net");
/**
* Use these queues to imitate the network stack
* <dl>
* <dt>input</dt><dd>going onto the network</dd>
* <dt>output</dt><dd>coming from the network</dd>
* </dl>
*/
final private BlockingQueue<ByteBuffer> input;
final private BlockingQueue<ByteBuffer> output;
final public AtomicBoolean throwClosedChannelException;
final public AtomicBoolean throwInterruptedException;
final public AtomicBoolean throwSocketException;
final public AtomicBoolean throwException;
public MockNetworkStack() {
this.input = new LinkedBlockingQueue<ByteBuffer>();
this.output = new LinkedBlockingQueue<ByteBuffer>();
this.throwInterruptedException = new AtomicBoolean(false);
this.throwClosedChannelException = new AtomicBoolean(false);
this.throwSocketException = new AtomicBoolean(false);
this.throwException = new AtomicBoolean(false);
logger.info("mock network stack created ");
}
/**
* This method works in conjunction with receive().
*/
public void putReceivable(ByteBuffer buf) {
buf.flip();
this.output.offer(buf);
logger.info("put of [{}] receivable [{}]", this.output.size(), buf);
}
/**
* This method is called by the MockChannel. It returns the next item in the
* Mock Network.
*
* @return
* @throws InterruptedException
* @throws ClosedChannelException
* @throws Exception
*/
public ByteBuffer receive() throws InterruptedException, ClosedChannelException, Exception {
logger.info("receive queue size {}", this.output.size());
if (this.throwInterruptedException.get()) {
throw new InterruptedException();
}
if (this.throwClosedChannelException.get()) {
throw new ClosedChannelException();
}
if (this.throwException.get()) {
throw new Exception("mock socket exception");
}
final ByteBuffer result = this.output.poll(50, TimeUnit.SECONDS);
logger.error("receive [{}]", result);
return result;
}
/**
* This method works in conjunction with send().
*/
public ByteBuffer getSent() {
try {
final ByteBuffer result = this.input.poll(5, TimeUnit.SECONDS);
logger.info("send queue size {}", this.input.size());
return result;
} catch (InterruptedException ex) {
logger.error("unsendable ", ex);
return null;
}
}
public void send(ByteBuffer buf) throws SocketException, Exception {
if (this.throwSocketException.get()) {
throw new SocketException("mock socket exception");
}
if (this.throwException.get()) {
throw new Exception("mock socket exception");
}
logger.info("send size [{}] [{}]",buf.position(), buf.array());
this.input.offer(buf);
logger.info("queue size [{}]", this.input.size());
}
private final static Charset charset = Charset.forName("UTF-8");
@SuppressWarnings("unused")
private final static CharsetEncoder encoder = charset.newEncoder();
private final static CharsetDecoder decoder = charset.newDecoder();
/**
* Decode the byte buffer into a string.
*
* @param buf
* @return
*/
public static String asString(ByteBuffer buf) {
if (buf == null) return "<null>";
final int pos = buf.position();
decoder.reset();
final CharBuffer cbuf = CharBuffer.allocate(buf.remaining());
decoder.decode(buf, cbuf, true);
decoder.flush(cbuf);
final String result = cbuf.toString();
buf.position(pos);
return result;
}
}