package org.simpleframework.transport.reactor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
import org.simpleframework.transport.trace.MockTrace;
import org.simpleframework.transport.trace.Trace;
public class DistributorTest extends TestCase {
private static final String PAYLOAD =
"POST /index.html HTTP/1.0\r\n"+
"Content-Type: multipart/form-data; boundary=AaB03x\r\n"+
"Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+
" \t\t image/png;\t\r\n\t"+
" q=1.0,*;q=0.1\r\n"+
"Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+
"Host: some.host.com \r\n"+
"Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+
"Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+
"\r\n" +
"--AaB03x\r\n"+
"Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+
"Content-Type: text/plain\r\n\r\n"+
"example contents of file1.txt\r\n"+
"--AaB03x\r\n"+
"Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+
"--BbC04y\r\n"+
"Content-Disposition: form-data; name='pics'; filename='file2.txt'\r\n"+
"Content-Type: text/plain\r\n\r\n"+
"example contents of file3.txt ...\r\n"+
"--BbC04y\r\n"+
"Content-Disposition: form-data; name='pics'; filename='file3.txt'\r\n"+
"Content-Type: text/plain\r\n\r\n"+
"example contents of file4.txt ...\r\n"+
"--BbC04y\r\n"+
"Content-Disposition: form-data; name='pics'; filename='file4.txt'\r\n"+
"Content-Type: text/plain\r\n\r\n"+
"example contents of file4.txt ...\r\n"+
"--BbC04y--\r\n"+
"--AaB03x--\r\n";
public class Client extends Thread {
private CountDownLatch latch;
private String message;
private int requests;
private int port;
public Client(CountDownLatch latch, String message, int port, int requests) throws Exception {
this.message = message;
this.requests = requests;
this.port = port;
this.latch = latch;
this.start();
}
public void run() {
try {
latch.await();
Socket socket = new Socket("localhost", port);
OutputStream out = socket.getOutputStream();
byte[] payload = message.getBytes();
for(int i = 0; i < requests; i++){
out.write(payload);
}
out.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
public class Worker implements Operation {
private BlockingQueue<Worker> done;
private Reactor reactor;
private SocketChannel channel;
private ByteBuffer buffer;
private String payload;
private int accumulate;
private long finish;
private long start;
private int id;
public Worker(BlockingQueue<Worker> done, Reactor reactor, SocketChannel channel, String payload, int id) throws Exception {
this.buffer = ByteBuffer.allocate(8192);
this.start = System.currentTimeMillis();
this.finish = start + 60000;
this.payload = payload;
this.channel = channel;
this.reactor = reactor;
this.done = done;
this.id = id;
}
public Trace getTrace() {
return new MockTrace();
}
public long getExpiry(TimeUnit unit) {
return unit.convert(finish - System.currentTimeMillis(), MILLISECONDS);
}
public int getAccumulate() {
return accumulate;
}
// XXX should this be executed in a thread!!!!???? yes...
public void cancel() {
System.err.println("############################# Worker has been canceled");
}
public void run() {
try {
// N.B Fundamental to performance
buffer.clear();
if(channel.isOpen()) {
int count = channel.read(buffer);
accumulate += count;
System.err.println("Worker-"+id+" read ["+count +"] of payload sized ["+payload.length()+"] took ["+(System.currentTimeMillis() -start)+"]");
if(count != -1) {
reactor.process(this, SelectionKey.OP_READ);
} else {
channel.close();
done.offer(this);
System.err.println("Worker-"+id+" Channel is closed after time ["+(System.currentTimeMillis() - start)+"] and read ["+accumulate+"]");
}
} else {
System.err.println("Worker-"+id+" Channel is closed after time ["+(System.currentTimeMillis() - start)+"] and read ["+accumulate+"]");
done.offer(this);
}
}catch(Exception e) {
e.printStackTrace();
}
}
public SocketChannel getChannel() {
return channel;
}
}
public class Server extends Thread {
private BlockingQueue<SocketChannel> ready;
private CountDownLatch latch;
private ServerSocketChannel server;
private Selector selector;
private int port;
public Server(CountDownLatch latch, BlockingQueue<SocketChannel> ready, int port) throws Exception {
this.server = ServerSocketChannel.open();
this.selector = Selector.open();
this.latch = latch;
this.port = port;
this.ready = ready;
this.start();
}
private void configure() throws Exception {
server.socket().bind(new InetSocketAddress(port));
server.configureBlocking(false);
}
public void run() {
try {
configure();
execute();
} catch(Exception e) {
e.printStackTrace();
}
}
private void execute() throws Exception {
SelectionKey serverKey = server.register(selector, SelectionKey.OP_ACCEPT);
latch.countDown();
while(true){
selector.select();
Set keys = selector.selectedKeys();
for(Iterator i = keys.iterator(); i.hasNext();){
SelectionKey key = (SelectionKey) i.next();
i.remove();
if(key != serverKey) {
return;
}
if(key.isAcceptable()) {
SocketChannel channel = server.accept();
channel.configureBlocking(false);
ready.offer(channel);
}
}
}
}
}
public static void main(String[] list) throws Exception {
new DistributorTest().testReactor();
}
public void testReactor() throws Exception {
testReactor(PAYLOAD, 200, 100, 10, 8123);
}
private void testReactor(String payload, int clients, int requests, int threads, int port) throws Exception {
BlockingQueue<Worker> done = new LinkedBlockingQueue<Worker>();
BlockingQueue<SocketChannel> ready = new LinkedBlockingQueue<SocketChannel>();
CountDownLatch latch = new CountDownLatch(1);
Server server = new Server(latch, ready, port);
Executor executor = Executors.newFixedThreadPool(10);
Reactor reactor = new ExecutorReactor(executor, 1);
long start = System.currentTimeMillis();
for(int i = 0; i < clients; i++) {
new Client(latch, payload, port, requests);
}
for(int i = 0; i < clients; i++) {
SocketChannel channel = ready.take();
Worker worker = new Worker(done, reactor, channel, payload, i);
reactor.process(worker);
}
int total = 0;
for(int i = 0; i < clients; i++) {
Worker worker = done.take();
int accumulate = worker.getAccumulate();
total += accumulate;
System.err.println("Accumulated ["+accumulate+"] of ["+(requests*payload.length())+"] closed ["+worker.getChannel().socket().isClosed()+"]");
}
System.err.println("Accumulated ["+total+"] of ["+(clients*requests*payload.length())+"]");
System.err.println("Total time to process ["+(clients*requests)+"] payloads from ["+clients+"] clients took ["+(System.currentTimeMillis() - start)+"]");
}
}