package edu.washington.cs.oneswarm.test.util;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;
/**
* Listens for incoming data and stores the last received data. Doesn't do anything
* with this data but provides methods to verify that it was received.
*/
public class TestReceivedServer implements Runnable {
public static Logger logger = Logger.getLogger(TestReceivedServer.class.getName());
class BasicServerHandler implements Runnable {
private final Socket client;
private TestReceivedServer server;
public BasicServerHandler(Socket client, TestReceivedServer server) {
this.client = client;
this.server = server;
}
@Override
public void run() {
InputStream in = null;
try {
byte[] allData = null;
byte[] buffer = new byte[1024];
in = client.getInputStream();
int read;
while ((read = in.read(buffer)) != -1) {
appendData(buffer, read);
logger.finest("read " + read + " bytes");
}
server.setData(allData);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
// Append the first length bytes to allData
private void appendData(byte[] bytes, int length) {
if (data == null) {
data = Arrays.copyOf(bytes, length);
} else {
int oldLen = data.length;
data = Arrays.copyOf(data, oldLen + bytes.length);
for (int i = 0; i < bytes.length; i++) {
data[i + oldLen] = bytes[i];
}
}
}
}
private int port;
private byte[] data;
private final Semaphore started = new Semaphore(0);
public TestReceivedServer(int port) {
this.port = port;
data = null;
}
public int waitForStart() throws InterruptedException {
started.acquire();
started.release();
return port;
}
public boolean hasData() {
// This should use a lock if server will actually be used with multiple clients
// simultaneously, not just one client for testing
return data != null;
}
public void clearData() {
// This should use a lock if server will actually be used with multiple clients
// simultaneously, not just one client for testing
data = null;
}
public byte[] getLatestData() {
// This should use a lock if server will actually be used with multiple clients
// simultaneously, not just one client for testing
return data;
}
private void setData(byte[] bytes) {
// This should use a lock if server will actually be used with multiple clients
// simultaneously, not just one client for testing
data = bytes;
}
@Override
public void run() {
ServerSocket ss = null;
try {
ss = new ServerSocket(port);
// If port 0 is specified the server will bind to a free port
// when that is the case, update the port field so be accurate.
this.port = ss.getLocalPort();
started.release();
while (true) {
Socket client = ss.accept();
logger.info("connection from: " + client.getRemoteSocketAddress());
Thread t = new Thread(new BasicServerHandler(client, this));
t.setDaemon(true);
t.setName("TestReceivedServer_" + port);
t.start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public Thread startDaemonThread(boolean blockUntilStarted) throws InterruptedException {
Thread serverThread = new Thread(this);
serverThread.setName("Listen server");
serverThread.setDaemon(true);
serverThread.start();
if (blockUntilStarted) {
waitForStart();
}
return serverThread;
}
}