// ======================================================================== // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.server.handler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationListener; import org.eclipse.jetty.continuation.ContinuationSupport; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.junit.After; import org.junit.Before; import org.junit.Test; public class StatisticsHandlerTest { private Server _server; private LocalConnector _connector; private LatchHandler _latchHandler; private StatisticsHandler _statsHandler; @Before public void init() throws Exception { _server = new Server(); _connector = new LocalConnector(); _server.addConnector(_connector); _connector.setStatsOn(true); _latchHandler = new LatchHandler(); _statsHandler = new StatisticsHandler(); _server.setHandler(_latchHandler); _latchHandler.setHandler(_statsHandler); } @After public void destroy() throws Exception { _server.stop(); _server.join(); } @Test public void testRequest() throws Exception { final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2)}; _statsHandler.setHandler(new AbstractHandler() { public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { request.setHandled(true); try { barrier[0].await(); barrier[1].await(); } catch (Exception x) { Thread.currentThread().interrupt(); throw (IOException)new IOException().initCause(x); } } }); _server.start(); String request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"; _connector.executeRequest(request); barrier[0].await(); assertEquals(1, _connector.getConnectionsOpen()); assertEquals(1, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getRequestsActiveMax()); assertEquals(1, _statsHandler.getDispatched()); assertEquals(1, _statsHandler.getDispatchedActive()); assertEquals(1, _statsHandler.getDispatchedActiveMax()); barrier[1].await(); boolean passed = _latchHandler.await(1000); assertTrue(passed); assertEquals(1, _statsHandler.getRequests()); assertEquals(0, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getRequestsActiveMax()); assertEquals(1, _statsHandler.getDispatched()); assertEquals(0, _statsHandler.getDispatchedActive()); assertEquals(1, _statsHandler.getDispatchedActiveMax()); assertEquals(0, _statsHandler.getSuspends()); assertEquals(0, _statsHandler.getResumes()); assertEquals(0, _statsHandler.getExpires()); assertEquals(1, _statsHandler.getResponses2xx()); _latchHandler.reset(); barrier[0].reset(); barrier[1].reset(); _connector.executeRequest(request); barrier[0].await(); assertEquals(2, _connector.getConnectionsOpen()); assertEquals(2, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getRequestsActiveMax()); assertEquals(2, _statsHandler.getDispatched()); assertEquals(1, _statsHandler.getDispatchedActive()); assertEquals(1, _statsHandler.getDispatchedActiveMax()); barrier[1].await(); passed = _latchHandler.await(1000); assertTrue(passed); assertEquals(2, _statsHandler.getRequests()); assertEquals(0, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getRequestsActiveMax()); assertEquals(2, _statsHandler.getDispatched()); assertEquals(0, _statsHandler.getDispatchedActive()); assertEquals(1, _statsHandler.getDispatchedActiveMax()); assertEquals(0, _statsHandler.getSuspends()); assertEquals(0, _statsHandler.getResumes()); assertEquals(0, _statsHandler.getExpires()); assertEquals(2, _statsHandler.getResponses2xx()); _latchHandler.reset(2); barrier[0]=new CyclicBarrier(3); barrier[1]=new CyclicBarrier(3); _connector.executeRequest(request); _connector.executeRequest(request); barrier[0].await(); assertEquals(4, _connector.getConnectionsOpen()); assertEquals(4, _statsHandler.getRequests()); assertEquals(2, _statsHandler.getRequestsActive()); assertEquals(2, _statsHandler.getRequestsActiveMax()); assertEquals(4, _statsHandler.getDispatched()); assertEquals(2, _statsHandler.getDispatchedActive()); assertEquals(2, _statsHandler.getDispatchedActiveMax()); barrier[1].await(); passed = _latchHandler.await(1000); assertTrue(passed); assertEquals(4, _statsHandler.getRequests()); assertEquals(0, _statsHandler.getRequestsActive()); assertEquals(2, _statsHandler.getRequestsActiveMax()); assertEquals(4, _statsHandler.getDispatched()); assertEquals(0, _statsHandler.getDispatchedActive()); assertEquals(2, _statsHandler.getDispatchedActiveMax()); assertEquals(0, _statsHandler.getSuspends()); assertEquals(0, _statsHandler.getResumes()); assertEquals(0, _statsHandler.getExpires()); assertEquals(4, _statsHandler.getResponses2xx()); } @Test public void testSuspendResume() throws Exception { final AtomicReference<Continuation> continuationHandle = new AtomicReference<Continuation>(); final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)}; _statsHandler.setHandler(new AbstractHandler() { public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { request.setHandled(true); try { barrier[0].await(); Thread.sleep(10); Continuation continuation = ContinuationSupport.getContinuation(httpRequest); if (continuationHandle.get() == null) { continuation.suspend(); continuationHandle.set(continuation); } } catch (Exception x) { Thread.currentThread().interrupt(); throw (IOException)new IOException().initCause(x); } finally { try { barrier[1].await(); } catch (Exception x) { x.printStackTrace(); Thread.currentThread().interrupt(); fail(); } } } }); _server.start(); String request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"; _connector.executeRequest(request); barrier[0].await(); assertEquals(1, _connector.getConnectionsOpen()); assertEquals(1, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getDispatched()); assertEquals(1, _statsHandler.getDispatchedActive()); barrier[1].await(); assertTrue(_latchHandler.await(1000)); assertNotNull(continuationHandle.get()); assertTrue(continuationHandle.get().isSuspended()); assertEquals(1, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getDispatched()); assertEquals(0, _statsHandler.getDispatchedActive()); Thread.sleep(10); _latchHandler.reset(); barrier[0].reset(); barrier[1].reset(); continuationHandle.get().addContinuationListener(new ContinuationListener() { public void onTimeout(Continuation continuation) { } public void onComplete(Continuation continuation) { try { barrier[2].await(); } catch(Exception e) {} } }); continuationHandle.get().resume(); barrier[0].await(); assertEquals(1, _connector.getConnectionsOpen()); assertEquals(1, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(2, _statsHandler.getDispatched()); assertEquals(1, _statsHandler.getDispatchedActive()); barrier[1].await(); assertTrue(_latchHandler.await(1000)); barrier[2].await(); assertEquals(1, _statsHandler.getRequests()); assertEquals(0, _statsHandler.getRequestsActive()); assertEquals(2, _statsHandler.getDispatched()); assertEquals(0, _statsHandler.getDispatchedActive()); assertEquals(1, _statsHandler.getSuspends()); assertEquals(1, _statsHandler.getResumes()); assertEquals(0, _statsHandler.getExpires()); assertEquals(1, _statsHandler.getResponses2xx()); assertTrue(_statsHandler.getRequestTimeTotal()>=30); assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMax()); assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMean(), 0.01); assertTrue(_statsHandler.getDispatchedTimeTotal()>=20); assertTrue(_statsHandler.getDispatchedTimeMean()+10<=_statsHandler.getDispatchedTimeTotal()); assertTrue(_statsHandler.getDispatchedTimeMax()+10<=_statsHandler.getDispatchedTimeTotal()); } @Test public void testSuspendExpire() throws Exception { final AtomicReference<Continuation> continuationHandle = new AtomicReference<Continuation>(); final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)}; _statsHandler.setHandler(new AbstractHandler() { public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { request.setHandled(true); try { barrier[0].await(); Thread.sleep(10); Continuation continuation = ContinuationSupport.getContinuation(httpRequest); if (continuationHandle.get() == null) { continuation.setTimeout(100); continuation.suspend(); continuationHandle.set(continuation); } } catch (Exception x) { Thread.currentThread().interrupt(); throw (IOException)new IOException().initCause(x); } finally { try { barrier[1].await(); } catch (Exception x) { x.printStackTrace(); Thread.currentThread().interrupt(); fail(); } } } }); _server.start(); String request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"; _connector.executeRequest(request); barrier[0].await(); assertEquals(1, _connector.getConnectionsOpen()); assertEquals(1, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getDispatched()); assertEquals(1, _statsHandler.getDispatchedActive()); barrier[1].await(); assertTrue(_latchHandler.await(1000)); assertNotNull(continuationHandle.get()); assertTrue(continuationHandle.get().isSuspended()); continuationHandle.get().addContinuationListener(new ContinuationListener() { public void onTimeout(Continuation continuation) { } public void onComplete(Continuation continuation) { try { barrier[2].await(); } catch(Exception e) {} } }); assertEquals(1, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getDispatched()); assertEquals(0, _statsHandler.getDispatchedActive()); _latchHandler.reset(); barrier[0].reset(); barrier[1].reset(); barrier[0].await(); assertEquals(1, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(2, _statsHandler.getDispatched()); assertEquals(1, _statsHandler.getDispatchedActive()); barrier[1].await(); assertTrue(_latchHandler.await(1000)); barrier[2].await(); assertEquals(1, _statsHandler.getRequests()); assertEquals(0, _statsHandler.getRequestsActive()); assertEquals(2, _statsHandler.getDispatched()); assertEquals(0, _statsHandler.getDispatchedActive()); assertEquals(1, _statsHandler.getSuspends()); assertEquals(1, _statsHandler.getResumes()); assertEquals(1, _statsHandler.getExpires()); assertEquals(1, _statsHandler.getResponses2xx()); assertTrue(_statsHandler.getRequestTimeTotal()>=30); assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMax()); assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMean(), 0.01); assertTrue(_statsHandler.getDispatchedTimeTotal()>=20); assertTrue(_statsHandler.getDispatchedTimeMean()+10<=_statsHandler.getDispatchedTimeTotal()); assertTrue(_statsHandler.getDispatchedTimeMax()+10<=_statsHandler.getDispatchedTimeTotal()); } @Test public void testSuspendComplete() throws Exception { final AtomicReference<Continuation> continuationHandle = new AtomicReference<Continuation>(); final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)}; _statsHandler.setHandler(new AbstractHandler() { public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { request.setHandled(true); try { barrier[0].await(); Thread.sleep(10); Continuation continuation = ContinuationSupport.getContinuation(httpRequest); if (continuationHandle.get() == null) { continuation.setTimeout(1000); continuation.suspend(); continuationHandle.set(continuation); } } catch (Exception x) { Thread.currentThread().interrupt(); throw (IOException)new IOException().initCause(x); } finally { try { barrier[1].await(); } catch (Exception x) { x.printStackTrace(); Thread.currentThread().interrupt(); fail(); } } } }); _server.start(); String request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"; _connector.executeRequest(request); barrier[0].await(); assertEquals(1, _connector.getConnectionsOpen()); assertEquals(1, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getDispatched()); assertEquals(1, _statsHandler.getDispatchedActive()); barrier[1].await(); assertTrue(_latchHandler.await(1000)); assertNotNull(continuationHandle.get()); assertTrue(continuationHandle.get().isSuspended()); continuationHandle.get().addContinuationListener(new ContinuationListener() { public void onTimeout(Continuation continuation) { } public void onComplete(Continuation continuation) { try { barrier[2].await(); } catch(Exception e) {} } }); assertEquals(1, _statsHandler.getRequests()); assertEquals(1, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getDispatched()); assertEquals(0, _statsHandler.getDispatchedActive()); Thread.sleep(10); continuationHandle.get().complete(); barrier[2].await(); assertEquals(1, _statsHandler.getRequests()); assertEquals(0, _statsHandler.getRequestsActive()); assertEquals(1, _statsHandler.getDispatched()); assertEquals(0, _statsHandler.getDispatchedActive()); assertEquals(1, _statsHandler.getSuspends()); assertEquals(0, _statsHandler.getResumes()); assertEquals(0, _statsHandler.getExpires()); assertEquals(1, _statsHandler.getResponses2xx()); assertTrue(_statsHandler.getRequestTimeTotal()>=20); assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMax()); assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMean(), 0.01); assertTrue(_statsHandler.getDispatchedTimeTotal()>=10); assertTrue(_statsHandler.getDispatchedTimeTotal()<_statsHandler.getRequestTimeTotal()); assertEquals(_statsHandler.getDispatchedTimeTotal(),_statsHandler.getDispatchedTimeMax()); assertEquals(_statsHandler.getDispatchedTimeTotal(),_statsHandler.getDispatchedTimeMean(), 0.01); } /** * This handler is external to the statistics handler and it is used to ensure that statistics handler's * handle() is fully executed before asserting its values in the tests, to avoid race conditions with the * tests' code where the test executes but the statistics handler has not finished yet. */ private static class LatchHandler extends HandlerWrapper { private volatile CountDownLatch _latch = new CountDownLatch(1); @Override public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { final CountDownLatch latch=_latch; try { super.handle(path, request, httpRequest, httpResponse); } finally { latch.countDown(); } } private void reset() { _latch=new CountDownLatch(1); } private void reset(int count) { _latch=new CountDownLatch(count); } private boolean await(long ms) throws InterruptedException { return _latch.await(ms, TimeUnit.MILLISECONDS); } } }