package examples.web;
import static uk.co.acuminous.julez.util.JulezSugar.*;
import static org.jboss.netty.channel.Channels.pipeline;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.HttpClientCodec;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.junit.Test;
import uk.co.acuminous.julez.event.handler.ThroughputMonitor;
import uk.co.acuminous.julez.runner.ConcurrentScenarioRunner;
import uk.co.acuminous.julez.scenario.BaseScenario;
import uk.co.acuminous.julez.scenario.Scenario;
import uk.co.acuminous.julez.scenario.ScenarioEvent;
import uk.co.acuminous.julez.scenario.ScenarioSource;
import uk.co.acuminous.julez.scenario.limiter.SizeLimiter;
import uk.co.acuminous.julez.scenario.source.ScenarioRepeater;
import uk.co.acuminous.julez.test.WebTestCase;
public class NettyWebTest extends WebTestCase {
@Test
public void demonstrateAConcurrentWebTestUsingNetty() {
ThroughputMonitor throughputMonitor = new ThroughputMonitor();
Scenario scenario = new NettyScenario().register(throughputMonitor);
ScenarioSource scenarios = new SizeLimiter().limit(new ScenarioRepeater(scenario)).to(100, SCENARIOS);
new ConcurrentScenarioRunner().register(throughputMonitor).queue(scenarios).allocate(10, THREADS).go();
System.out.println("\nNetty Throughput\n----------------");
System.out.println(throughputMonitor.getThroughput());
}
class NettyScenario extends BaseScenario {
@Override
public void run() {
handler.onEvent(eventFactory.begin());
HttpClient client = new HttpClient(this);
try
{
client.init();
client.get("http://localhost:28081/");
} catch (Exception e) {
error(e.getMessage());
} finally {
client.shutdown();
}
handler.onEvent(eventFactory.end());
}
public void pass() {
handler.onEvent(eventFactory.pass());
}
public void fail(Integer status, String message) {
ScenarioEvent event = eventFactory.fail();
event.getData().put("statusCode", String.valueOf(status));
event.getData().put("message", message);
handler.onEvent(event);
}
public void error(String message) {
ScenarioEvent event = eventFactory.error();
event.getData().put("message", message);
handler.onEvent(event);
}
}
class HttpClient {
private String host = "localhost";
private int port = 28081;
private Channel channel;
private ClientBootstrap bootstrap;
private final NettyScenario scenario;
public HttpClient(NettyScenario scenario) {
this.scenario = scenario;
}
public void init() {
bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newSingleThreadExecutor(), Executors.newSingleThreadExecutor()));
bootstrap.setPipelineFactory(new HttpClientPipelineFactory(scenario));
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
channel = future.awaitUninterruptibly().getChannel();
if (!future.isSuccess()) {
future.getCause().printStackTrace();
bootstrap.releaseExternalResources();
return;
}
}
public void get(String uri) {
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
request.setHeader(HttpHeaders.Names.HOST, host);
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
channel.write(request);
channel.getCloseFuture().awaitUninterruptibly();
}
public void shutdown() {
bootstrap.releaseExternalResources();
}
}
class HttpClientPipelineFactory implements ChannelPipelineFactory {
private final NettyScenario scenario;
public HttpClientPipelineFactory(NettyScenario scenario) {
this.scenario = scenario;
}
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = pipeline();
pipeline.addLast("codec", new HttpClientCodec());
pipeline.addLast("handler", new HttpResponseHandler(scenario));
return pipeline;
}
}
class HttpResponseHandler extends SimpleChannelUpstreamHandler {
private boolean readingChunks;
private final NettyScenario scenario;
public HttpResponseHandler(NettyScenario scenario) {
this.scenario = scenario;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if (!readingChunks) {
HttpResponse response = (HttpResponse) e.getMessage();
if (response.getStatus().getCode() == 200) {
scenario.pass();
} else {
scenario.fail(response.getStatus().getCode(), response.getStatus().getReasonPhrase());
}
if (response.isChunked()) {
readingChunks = true;
}
}
}
}
}