diff --git a/VERSION.txt b/VERSION.txt index 2627bb38c1c..1620387e638 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -12,6 +12,7 @@ jetty-7.0.1-SNAPSHOT + 292642 Fix errors in embedded Jetty examples + 292825 Continuations ISE rather than ignore bad transitions + 292546 Proactively enforce HttpClient idle timeout + + 293222 Improved StatisticsHandler for async + 293506 Unable to use jconsole with Jetty when running with security manager + 293557 Add "jad" mime mapping + JETTY-937 More JVM bug work arounds. Insert pause if all else fails diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 8e9bae656b7..5ccc1e8006a 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -29,6 +29,7 @@ + org.apache.maven.plugins maven-remote-resources-plugin diff --git a/jetty-jmx/src/main/resources/org/eclipse/jetty/server/handler/jmx/StatisticsHandler-mbean.properties b/jetty-jmx/src/main/resources/org/eclipse/jetty/server/handler/jmx/StatisticsHandler-mbean.properties index 46adfc9ea7e..a39bd2856f5 100644 --- a/jetty-jmx/src/main/resources/org/eclipse/jetty/server/handler/jmx/StatisticsHandler-mbean.properties +++ b/jetty-jmx/src/main/resources/org/eclipse/jetty/server/handler/jmx/StatisticsHandler-mbean.properties @@ -2,14 +2,22 @@ StatisticsHandler: Request Statistics gathering statsOnMs: Time in milliseconds stats have been collected for. statsReset(): Resets statistics. requests: Number of requests since statsReset() called. -requestsExpired: Number of requests expired since statsReset() called. -requestsResumed: Number of requests resumed since statsReset() called. -requestTimeAverage: Average time in milliseconds of request handling since statsReset() called. -requestTimeMin: Minimum time in milliseconds of request handling since statsReset() called. -requestTimeMax: Maximum time in milliseconds of request handling since statsReset() called. -requestTimeTotal: Total time in milliseconds of all request handling since statsReset() called. requestsActive: Number of requests currently active since statsReset() called. requestsActiveMax: Maximum number of active requests since statsReset() called. +requestTimeMax: Maximum time in milliseconds of request handling since statsReset() called. +requestTimeTotal: Total time in milliseconds of all request handling since statsReset() called. +dispatched: Number of dispatches since statsReset() called. +dispatchedActive: Number of dispatches currently active since statsReset() called. +dispatchedActiveMax: Maximum number of active dispatches since statsReset() called. +dispatchedTimeMax: Maximum time in milliseconds of dispatched handling since statsReset() called. +dispatchedTimeTotal: Total time in milliseconds of all dispatched handling since statsReset() called. +suspends: Number of requests suspended since statsReset() called. +suspendsActive: Number of dispatches currently active since statsReset() called. +suspendsActiveMax: Maximum number of active dispatches since statsReset() called. +resumes: Number of requests resumed since statsReset() called. +expires: Number of requests expired since statsReset() called. +requestTimeAverage: Average time in milliseconds of request handling since statsReset() called. +dispatchedTimeAverage: Average time in milliseconds of dispatch handling since statsReset() called. responses1xx: Number of responses with a 1xx status since statsReset() called. responses2xx: Number of responses with a 2xx status since statsReset() called. responses3xx: Number of responses with a 3xx status since statsReset() called. diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java index c0829d48a84..74ebea2b0d6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java @@ -675,6 +675,12 @@ public class AsyncContinuation implements AsyncContext, Continuation dispatch(); } + /* ------------------------------------------------------------ */ + public Request getBaseRequest() + { + return _connection.getRequest(); + } + /* ------------------------------------------------------------ */ public ServletRequest getRequest() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java index 535eb688ef4..62ccde9ab44 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java @@ -21,46 +21,87 @@ 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.server.AsyncContinuation; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; public class StatisticsHandler extends HandlerWrapper { - private transient final AtomicLong _statsStartedAt = new AtomicLong(); - private transient final AtomicInteger _requests = new AtomicInteger(); - private transient final AtomicInteger _resumedRequests = new AtomicInteger(); - private transient final AtomicInteger _expiredRequests = new AtomicInteger(); - private transient final AtomicLong _requestMinTime = new AtomicLong(); - private transient final AtomicLong _requestMaxTime = new AtomicLong(); - private transient final AtomicLong _requestTotalTime = new AtomicLong(); - private transient final AtomicLong _suspendMinTime = new AtomicLong(); - private transient final AtomicLong _suspendTotalTime = new AtomicLong(); - private transient final AtomicInteger _requestsActive = new AtomicInteger(); - private transient final AtomicInteger _requestsMaxActive = new AtomicInteger(); - private transient final AtomicInteger _responses1xx = new AtomicInteger(); - private transient final AtomicInteger _responses2xx = new AtomicInteger(); - private transient final AtomicInteger _responses3xx = new AtomicInteger(); - private transient final AtomicInteger _responses4xx = new AtomicInteger(); - private transient final AtomicInteger _responses5xx = new AtomicInteger(); - private transient final AtomicLong _responsesTotalBytes = new AtomicLong(); + private final AtomicLong _statsStartedAt = new AtomicLong(); + + private final AtomicInteger _requests = new AtomicInteger(); + private final AtomicInteger _requestsActive = new AtomicInteger(); + private final AtomicInteger _requestsActiveMax = new AtomicInteger(); + private final AtomicLong _requestTimeMax = new AtomicLong(); + private final AtomicLong _requestTimeTotal = new AtomicLong(); + private final AtomicInteger _dispatched = new AtomicInteger(); + private final AtomicInteger _dispatchedActive = new AtomicInteger(); + private final AtomicInteger _dispatchedActiveMax = new AtomicInteger(); + private final AtomicLong _dispatchedTimeMax = new AtomicLong(); + private final AtomicLong _dispatchedTimeTotal = new AtomicLong(); + + private final AtomicInteger _suspends = new AtomicInteger(); + private final AtomicInteger _suspendsActive = new AtomicInteger(); + private final AtomicInteger _suspendsActiveMax = new AtomicInteger(); + private final AtomicInteger _resumes = new AtomicInteger(); + private final AtomicInteger _expires = new AtomicInteger(); + + private final AtomicInteger _responses1xx = new AtomicInteger(); + private final AtomicInteger _responses2xx = new AtomicInteger(); + private final AtomicInteger _responses3xx = new AtomicInteger(); + private final AtomicInteger _responses4xx = new AtomicInteger(); + private final AtomicInteger _responses5xx = new AtomicInteger(); + private final AtomicLong _responsesTotalBytes = new AtomicLong(); + + private final ContinuationListener _onCompletion = new ContinuationListener() + { + public void onComplete(Continuation continuation) + { + final Request request = ((AsyncContinuation)continuation).getBaseRequest(); + final long elapsed = System.currentTimeMillis()-request.getTimeStamp(); + + _requestsActive.decrementAndGet(); + _requests.incrementAndGet(); + updateMax(_requestTimeMax, elapsed); + _requestTimeTotal.addAndGet(elapsed); + updateResponse(request); + if (!continuation.isResumed()) + _suspendsActive.decrementAndGet(); + } + + public void onTimeout(Continuation continuation) + { + _expires.incrementAndGet(); + } + }; + /** * Resets the current request statistics. */ public void statsReset() { _statsStartedAt.set(System.currentTimeMillis()); + _requests.set(0); - _resumedRequests.set(0); - _expiredRequests.set(0); - _requestMinTime.set(Long.MAX_VALUE); - _requestMaxTime.set(0L); - _requestTotalTime.set(0L); - _suspendMinTime.set(Long.MAX_VALUE); - _suspendTotalTime.set(0L); _requestsActive.set(0); - _requestsMaxActive.set(0); + _requestsActiveMax.set(0); + _requestTimeMax.set(0L); + _requestTimeTotal.set(0L); + + _dispatched.set(0); + _dispatchedActive.set(0); + _dispatchedActiveMax.set(0); + _dispatchedTimeMax.set(0L); + _dispatchedTimeTotal.set(0L); + + _suspends.set(0); + _suspendsActive.set(0); + _suspendsActiveMax.set(0); + _resumes.set(0); + _expires.set(0); _responses1xx.set(0); _responses2xx.set(0); _responses3xx.set(0); @@ -91,40 +132,26 @@ public class StatisticsHandler extends HandlerWrapper } } - private void updateMin(AtomicLong valueHolder, long value) - { - long oldValue = valueHolder.get(); - while (value < oldValue) - { - if (valueHolder.compareAndSet(oldValue, value)) - break; - oldValue = valueHolder.get(); - } - } - @Override public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { - _requests.incrementAndGet(); + updateMax(_dispatchedActiveMax, _dispatchedActive.incrementAndGet()); - int activeRequests = _requestsActive.incrementAndGet(); - updateMax(_requestsMaxActive, activeRequests); - - // The order of the ifs is important, as a continuation can be resumed and expired - // We test first if it's expired, and then if it's resumed + final long start; AsyncContinuation continuation = request.getAsyncContinuation(); - if (continuation.isExpired()) + if (continuation.isInitial()) { - _expiredRequests.incrementAndGet(); + // new request + updateMax(_requestsActiveMax, _requestsActive.incrementAndGet()); + start = request.getTimeStamp(); } - else if (continuation.isResumed()) + else { - _resumedRequests.incrementAndGet(); - - long initialTime = request.getTimeStamp(); - long suspendTime = System.currentTimeMillis() - initialTime; - updateMin(_suspendMinTime, suspendTime); - _suspendTotalTime.addAndGet(suspendTime); + // resumed request + start = System.currentTimeMillis(); + _suspendsActive.decrementAndGet(); + if (continuation.isResumed()) + _resumes.incrementAndGet(); } try @@ -133,23 +160,37 @@ public class StatisticsHandler extends HandlerWrapper } finally { - _requestsActive.decrementAndGet(); - - if (!continuation.isSuspended()) + final long now = System.currentTimeMillis(); + final long dispatched=now-start; + + _dispatchedActive.decrementAndGet(); + _dispatched.incrementAndGet(); + + _dispatchedTimeTotal.addAndGet(dispatched); + updateMax(_dispatchedTimeMax, dispatched); + + if (continuation.isSuspended()) { + if (continuation.isInitial()) + continuation.addContinuationListener(_onCompletion); + _suspends.incrementAndGet(); + updateMax(_suspendsActiveMax, _suspendsActive.incrementAndGet()); + } + else if (continuation.isInitial()) + { + _requestsActive.decrementAndGet(); + _requests.incrementAndGet(); + + updateMax(_requestTimeMax, dispatched); + _requestTimeTotal.addAndGet(dispatched); updateResponse(request); } + // else onCompletion will handle it. } } private void updateResponse(Request request) { - long elapsed = System.currentTimeMillis() - request.getTimeStamp(); - - updateMin(_requestMinTime, elapsed); - updateMax(_requestMaxTime, elapsed); - _requestTotalTime.addAndGet(elapsed); - Response response = request.getResponse(); switch (response.getStatus() / 100) { @@ -171,7 +212,6 @@ public class StatisticsHandler extends HandlerWrapper default: break; } - _responsesTotalBytes.addAndGet(response.getContentCount()); } @@ -184,9 +224,9 @@ public class StatisticsHandler extends HandlerWrapper /** * @return the number of requests handled by this handler - * since {@link #statsReset()} was last called, including - * resumed requests - * @see #getRequestsResumed() + * since {@link #statsReset()} was last called, excluding + * active requests + * @see #getResumes() */ public int getRequests() { @@ -208,25 +248,145 @@ public class StatisticsHandler extends HandlerWrapper */ public int getRequestsActiveMax() { - return _requestsMaxActive.get(); + return _requestsActiveMax.get(); } /** - * @return the number of requests that have been resumed - * @see #getRequestsExpired() + * @return the maximum time (in milliseconds) of request handling + * since {@link #statsReset()} was last called. */ - public int getRequestsResumed() + public long getRequestTimeMax() { - return _resumedRequests.get(); + return _requestTimeMax.get(); + } + + /** + * @return the total time (in milliseconds) of requests handling + * since {@link #statsReset()} was last called. + */ + public long getRequestTimeTotal() + { + return _requestTimeTotal.get(); + } + + /** + * @return the average time (in milliseconds) of request handling + * since {@link #statsReset()} was last called. + * @see #getRequestTimeTotal() + * @see #getRequests() + */ + public long getRequestTimeAverage() + { + int requests = getRequests(); + return requests == 0 ? 0 : getRequestTimeTotal() / requests; + } + + /** + * @return the number of dispatches seen by this handler + * since {@link #statsReset()} was last called, excluding + * active dispatches + */ + public int getDispatched() + { + return _dispatched.get(); + } + + /** + * @return the number of dispatches currently in this handler + * since {@link #statsReset()} was last called, including + * resumed requests + */ + public int getDispatchedActive() + { + return _dispatchedActive.get(); + } + + /** + * @return the max number of dispatches currently in this handler + * since {@link #statsReset()} was last called, including + * resumed requests + */ + public int getDispatchedActiveMax() + { + return _dispatchedActiveMax.get(); + } + + /** + * @return the maximum time (in milliseconds) of request dispatch + * since {@link #statsReset()} was last called. + */ + public long getDispatchedTimeMax() + { + return _dispatchedTimeMax.get(); + } + + /** + * @return the total time (in milliseconds) of requests handling + * since {@link #statsReset()} was last called. + */ + public long getDispatchedTimeTotal() + { + return _dispatchedTimeTotal.get(); + } + + /** + * @return the average time (in milliseconds) of request handling + * since {@link #statsReset()} was last called. + * @see #getRequestTimeTotal() + * @see #getRequests() + */ + public long getDispatchedTimeAverage() + { + int requests = getDispatched(); + return requests == 0 ? 0 : getDispatchedTimeTotal() / requests; + } + + + /** + * @return the number of requests handled by this handler + * since {@link #statsReset()} was last called, including + * resumed requests + * @see #getResumes() + */ + public int getSuspends() + { + return _suspends.get(); + } + + /** + * @return the number of requests currently suspended. + * since {@link #statsReset()} was last called. + */ + public int getSuspendsActive() + { + return _suspendsActive.get(); + } + + /** + * @return the maximum number of current suspended requests + * since {@link #statsReset()} was last called. + */ + public int getSuspendsActiveMax() + { + return _suspendsActiveMax.get(); + } + + /** + * @return the number of requests that have been resumed + * @see #getExpires() + */ + public int getResumes() + { + return _resumes.get(); } /** * @return the number of requests that expired while suspended. - * @see #getRequestsResumed() + * @see #getResumes() */ - public int getRequestsExpired() + public int getExpires() { - return _expiredRequests.get(); + return _expires.get(); } /** @@ -281,46 +441,7 @@ public class StatisticsHandler extends HandlerWrapper { return System.currentTimeMillis() - _statsStartedAt.get(); } - - /** - * @return the minimum time (in milliseconds) of request handling - * since {@link #statsReset()} was last called. - */ - public long getRequestTimeMin() - { - return _requestMinTime.get(); - } - - /** - * @return the maximum time (in milliseconds) of request handling - * since {@link #statsReset()} was last called. - */ - public long getRequestTimeMax() - { - return _requestMaxTime.get(); - } - - /** - * @return the total time (in milliseconds) of requests handling - * since {@link #statsReset()} was last called. - */ - public long getRequestTimeTotal() - { - return _requestTotalTime.get(); - } - - /** - * @return the average time (in milliseconds) of request handling - * since {@link #statsReset()} was last called. - * @see #getRequestTimeTotal() - * @see #getRequests() - */ - public long getRequestTimeAverage() - { - int requests = getRequests(); - return requests == 0 ? 0 : getRequestTimeTotal() / requests; - } - + /** * @return the total bytes of content sent in responses */ @@ -328,22 +449,45 @@ public class StatisticsHandler extends HandlerWrapper { return _responsesTotalBytes.get(); } + + public String toStatsHTML() + { + StringBuilder sb = new StringBuilder(); - /** - * @return the minimum time (in milliseconds) of request suspension - * since {@link #statsReset()} was last called. - */ - public long getSuspendedTimeMin() - { - return _suspendMinTime.get(); - } + sb.append("

Statistics:

\n"); + sb.append("Statistics gathering started ").append(getStatsOnMs()).append("ms ago").append("
\n"); + + sb.append("

Requests:

\n"); + sb.append("Total requests: ").append(getRequests()).append("
\n"); + sb.append("Active requests: ").append(getRequestsActive()).append("
\n"); + sb.append("Max active requests: ").append(getRequestsActiveMax()).append("
\n"); + sb.append("Total requests time: ").append(getRequestTimeTotal()).append("
\n"); + sb.append("Average request time: ").append(getRequestTimeAverage()).append("
\n"); + sb.append("Max request time: ").append(getRequestTimeMax()).append("
\n"); + + + sb.append("

Dispatches:

\n"); + sb.append("Total dispatched: ").append(getDispatched()).append("
\n"); + sb.append("Active dispatched: ").append(getDispatchedActive()).append("
\n"); + sb.append("Max active dispatched: ").append(getDispatchedActiveMax()).append("
\n"); + sb.append("Total dispatched time: ").append(getDispatchedTimeTotal()).append("
\n"); + sb.append("Average dispatched time: ").append(getDispatchedTimeAverage()).append("
\n"); + sb.append("Max dispatched time: ").append(getDispatchedTimeMax()).append("
\n"); + + + sb.append("Total requests suspended: ").append(getSuspends()).append("
\n"); + sb.append("Total requests expired: ").append(getExpires()).append("
\n"); + sb.append("Total requests resumed: ").append(getResumes()).append("
\n"); + + sb.append("

Responses:

\n"); + sb.append("1xx responses: ").append(getResponses1xx()).append("
\n"); + sb.append("2xx responses: ").append(getResponses2xx()).append("
\n"); + sb.append("3xx responses: ").append(getResponses3xx()).append("
\n"); + sb.append("4xx responses: ").append(getResponses4xx()).append("
\n"); + sb.append("5xx responses: ").append(getResponses5xx()).append("
\n"); + sb.append("Bytes sent total: ").append(getResponsesBytesTotal()).append("
\n"); + + return sb.toString(); - /** - * @return the total time (in milliseconds) of request suspension - * since {@link #statsReset()} was last called. - */ - public long getSuspendedTimeTotal() - { - return _suspendTotalTime.get(); } } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java index 0907041b8e7..01ebc318eca 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java @@ -15,6 +15,7 @@ package org.eclipse.jetty.server.handler; 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; @@ -25,6 +26,7 @@ import javax.servlet.http.HttpServletResponse; import junit.framework.TestCase; 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; @@ -59,139 +61,10 @@ public class StatisticsHandlerTest extends TestCase _server.join(); } - public void testSuspendResume() throws Exception + public void testRequest() throws Exception { - final long sleep = 500; - final AtomicReference continuationHandle = new AtomicReference(); - _statsHandler.setHandler(new AbstractHandler() - { - public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException - { - request.setHandled(true); - - Continuation continuation = ContinuationSupport.getContinuation(httpRequest); - if (continuationHandle.get() == null) - { - continuation.suspend(); - continuationHandle.set(continuation); - try - { - Thread.sleep(sleep); - } - catch (InterruptedException 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); - boolean passed = _latchHandler.await(1000); - assertTrue(passed); - assertNotNull(continuationHandle.get()); - assertTrue(continuationHandle.get().isSuspended()); - - continuationHandle.get().resume(); - passed = _latchHandler.await(1000); - assertTrue(passed); - - assertEquals(2, _statsHandler.getRequests()); - assertEquals(1, _statsHandler.getRequestsResumed()); - assertEquals(0, _statsHandler.getRequestsExpired()); - assertEquals(1, _statsHandler.getResponses2xx()); - assertTrue(sleep <= _statsHandler.getSuspendedTimeMin()); - assertEquals(_statsHandler.getSuspendedTimeMin(), _statsHandler.getSuspendedTimeTotal()); - } - - public void testSuspendExpire() throws Exception - { - final long timeout = 1000; - final AtomicReference continuationHandle = new AtomicReference(); - _statsHandler.setHandler(new AbstractHandler() - { - public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException - { - request.setHandled(true); - - Continuation continuation = ContinuationSupport.getContinuation(httpRequest); - System.out.println("continuation = " + continuation); - if (continuationHandle.get() == null) - { - continuation.setTimeout(timeout); - continuation.suspend(); - continuationHandle.set(continuation); - } - } - }); - _server.start(); - - String request = "GET / HTTP/1.1\r\n" + - "Host: localhost\r\n" + - "\r\n"; - _connector.executeRequest(request); - boolean passed = _latchHandler.await(1000); - assertTrue(passed); - assertNotNull(continuationHandle.get()); - assertTrue(continuationHandle.get().isSuspended()); - - Thread.sleep(timeout); - - passed = _latchHandler.await(1000); - assertTrue(passed); - - assertEquals(2, _statsHandler.getRequests()); - assertEquals(0, _statsHandler.getRequestsResumed()); - assertEquals(1, _statsHandler.getRequestsExpired()); - assertEquals(1, _statsHandler.getResponses2xx()); - } - - public void testSuspendComplete() throws Exception - { - final AtomicReference continuationHandle = new AtomicReference(); - _statsHandler.setHandler(new AbstractHandler() - { - public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException - { - request.setHandled(true); - - Continuation continuation = ContinuationSupport.getContinuation(httpRequest); - if (continuationHandle.get() == null) - { - continuation.suspend(); - continuationHandle.set(continuation); - } - } - }); - _server.start(); - - String request = "GET / HTTP/1.1\r\n" + - "Host: localhost\r\n" + - "\r\n"; - _connector.executeRequest(request); - boolean passed = _latchHandler.await(1000); - assertTrue(passed); - assertNotNull(continuationHandle.get()); - assertTrue(continuationHandle.get().isSuspended()); - - continuationHandle.get().complete(); - - assertEquals(1, _statsHandler.getRequests()); - assertEquals(0, _statsHandler.getRequestsResumed()); - assertEquals(0, _statsHandler.getRequestsExpired()); - // TODO: complete callback not implemented - // Commented to pass the tests -// assertEquals(1, _statsHandler.getResponses2xx()); - } - - public void testRequestTimes() throws Exception - { - final long sleep = 1000; + 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 @@ -199,9 +72,11 @@ public class StatisticsHandlerTest extends TestCase request.setHandled(true); try { - Thread.sleep(sleep); + barrier[0].await(); + barrier[1].await(); + } - catch (InterruptedException x) + catch (Exception x) { Thread.currentThread().interrupt(); throw (IOException)new IOException().initCause(x); @@ -213,24 +88,452 @@ public class StatisticsHandlerTest extends TestCase String request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"; - _connector.getResponses(request); + _connector.executeRequest(request); + barrier[0].await(); + + assertEquals(0, _statsHandler.getRequests()); + assertEquals(1, _statsHandler.getRequestsActive()); + assertEquals(1, _statsHandler.getRequestsActiveMax()); + + assertEquals(0, _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()); - assertTrue(sleep <= _statsHandler.getRequestTimeMin()); - assertEquals(_statsHandler.getRequestTimeMin(), _statsHandler.getRequestTimeMax()); - assertEquals(_statsHandler.getRequestTimeMin(), _statsHandler.getRequestTimeTotal()); - assertEquals(_statsHandler.getRequestTimeMin(), _statsHandler.getRequestTimeAverage()); + + _latchHandler.reset(); + barrier[0].reset(); + barrier[1].reset(); + + _connector.executeRequest(request); - _connector.getResponses(request); + barrier[0].await(); + + 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(); + 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()); - assertTrue(sleep <= _statsHandler.getRequestTimeMin()); - assertTrue(sleep <= _statsHandler.getRequestTimeAverage()); - assertTrue(_statsHandler.getRequestTimeTotal() >= 2 * sleep); + + _latchHandler.reset(2); + barrier[0]=new CyclicBarrier(3); + barrier[1]=new CyclicBarrier(3); + + _connector.executeRequest(request); + _connector.executeRequest(request); + + barrier[0].await(); + + assertEquals(2, _statsHandler.getRequests()); + assertEquals(2, _statsHandler.getRequestsActive()); + assertEquals(2, _statsHandler.getRequestsActiveMax()); + + assertEquals(2, _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()); + + } + public void testSuspendResume() throws Exception + { + final AtomicReference continuationHandle = new AtomicReference(); + 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) + { + 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(0, _statsHandler.getRequests()); + assertEquals(1, _statsHandler.getRequestsActive()); + + assertEquals(0, _statsHandler.getDispatched()); + assertEquals(1, _statsHandler.getDispatchedActive()); + + barrier[1].await(); + assertTrue(_latchHandler.await(1000)); + assertNotNull(continuationHandle.get()); + assertTrue(continuationHandle.get().isSuspended()); + + assertEquals(0, _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(0, _statsHandler.getRequests()); + assertEquals(1, _statsHandler.getRequestsActive()); + assertEquals(1, _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.getRequestTimeAverage()); + + assertTrue(_statsHandler.getDispatchedTimeTotal()>=20); + assertTrue(_statsHandler.getDispatchedTimeAverage()+10<=_statsHandler.getDispatchedTimeTotal()); + assertTrue(_statsHandler.getDispatchedTimeMax()+10<=_statsHandler.getDispatchedTimeTotal()); + + } + + public void testSuspendExpire() throws Exception + { + final AtomicReference continuationHandle = new AtomicReference(); + 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(10); + 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) + { + 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(0, _statsHandler.getRequests()); + assertEquals(1, _statsHandler.getRequestsActive()); + + assertEquals(0, _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(0, _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(0, _statsHandler.getRequests()); + assertEquals(1, _statsHandler.getRequestsActive()); + assertEquals(1, _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.getRequestTimeAverage()); + + assertTrue(_statsHandler.getDispatchedTimeTotal()>=20); + assertTrue(_statsHandler.getDispatchedTimeAverage()+10<=_statsHandler.getDispatchedTimeTotal()); + assertTrue(_statsHandler.getDispatchedTimeMax()+10<=_statsHandler.getDispatchedTimeTotal()); + + } + + public void testSuspendComplete() throws Exception + { + final AtomicReference continuationHandle = new AtomicReference(); + 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) + { + 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(0, _statsHandler.getRequests()); + assertEquals(1, _statsHandler.getRequestsActive()); + + assertEquals(0, _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(0, _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.getRequestTimeAverage()); + + assertTrue(_statsHandler.getDispatchedTimeTotal()>=10); + assertTrue(_statsHandler.getDispatchedTimeTotal()<_statsHandler.getRequestTimeTotal()); + assertEquals(_statsHandler.getDispatchedTimeTotal(),_statsHandler.getDispatchedTimeMax()); + assertEquals(_statsHandler.getDispatchedTimeTotal(),_statsHandler.getDispatchedTimeAverage()); + } + + /** * 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 @@ -238,11 +541,12 @@ public class StatisticsHandlerTest extends TestCase */ private static class LatchHandler extends HandlerWrapper { - private volatile CountDownLatch latch = new CountDownLatch(1); + 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); @@ -253,11 +557,19 @@ public class StatisticsHandlerTest extends TestCase } } + private void reset() + { + _latch=new CountDownLatch(1); + } + + private void reset(int count) + { + _latch=new CountDownLatch(count); + } + private boolean await(long ms) throws InterruptedException { - boolean result = latch.await(ms, TimeUnit.MILLISECONDS); - latch = new CountDownLatch(1); - return result; + return _latch.await(ms, TimeUnit.MILLISECONDS); } } } diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java index 79ed8567ba0..3bb6a82eec5 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java @@ -111,17 +111,24 @@ public class StatisticsServlet extends HttpServlet sb.append(" \n"); sb.append(" ").append(_statsHandler.getStatsOnMs()).append("\n"); + sb.append(" ").append(_statsHandler.getRequests()).append("\n"); - sb.append(" ").append(_statsHandler.getRequestsExpired()).append("\n"); - sb.append(" ").append(_statsHandler.getRequestsResumed()).append("\n"); sb.append(" ").append(_statsHandler.getRequestsActive()).append("\n"); sb.append(" ").append(_statsHandler.getRequestsActiveMax()).append("\n"); sb.append(" ").append(_statsHandler.getRequestTimeTotal()).append("\n"); sb.append(" ").append(_statsHandler.getRequestTimeAverage()).append("\n"); - sb.append(" ").append(_statsHandler.getRequestTimeMin()).append("\n"); sb.append(" ").append(_statsHandler.getRequestTimeMax()).append("\n"); - sb.append(" ").append(_statsHandler.getSuspendedTimeMin()).append("\n"); - sb.append(" ").append(_statsHandler.getSuspendedTimeTotal()).append("\n"); + + sb.append(" ").append(_statsHandler.getDispatched()).append("\n"); + sb.append(" ").append(_statsHandler.getDispatchedActive()).append("\n"); + sb.append(" ").append(_statsHandler.getDispatchedActiveMax()).append("\n"); + sb.append(" ").append(_statsHandler.getDispatchedTimeTotal()).append("\n"); + sb.append(" ").append(_statsHandler.getDispatchedTimeAverage()).append("\n"); + sb.append(" ").append(_statsHandler.getDispatchedTimeMax()).append("\n"); + + sb.append(" ").append(_statsHandler.getSuspends()).append("\n"); + sb.append(" ").append(_statsHandler.getExpires()).append("\n"); + sb.append(" ").append(_statsHandler.getResumes()).append("\n"); sb.append(" \n"); sb.append(" \n"); @@ -173,32 +180,8 @@ public class StatisticsServlet extends HttpServlet private void sendTextResponse(HttpServletResponse response) throws IOException { - StringBuilder sb = new StringBuilder(); - - sb.append("

Statistics:

\n"); - - sb.append("

Requests:

\n"); - sb.append("Statistics gathering started ").append(_statsHandler.getStatsOnMs()).append("ms ago").append("
\n"); - sb.append("Total requests: ").append(_statsHandler.getRequests()).append("
\n"); - sb.append("Total requests expired: ").append(_statsHandler.getRequestsExpired()).append("
\n"); - sb.append("Total requests resumed: ").append(_statsHandler.getRequestsResumed()).append("
\n"); - sb.append("Current requests active: ").append(_statsHandler.getRequestsActive()).append("
\n"); - sb.append("Max concurrent requests active: ").append(_statsHandler.getRequestsActiveMax()).append("
\n"); - sb.append("Total requests time: ").append(_statsHandler.getRequestTimeTotal()).append("
\n"); - sb.append("Average request time: ").append(_statsHandler.getRequestTimeAverage()).append("
\n"); - sb.append("Min request time: ").append(_statsHandler.getRequestTimeMin()).append("
\n"); - sb.append("Max request time: ").append(_statsHandler.getRequestTimeMax()).append("
\n"); - sb.append("Min suspended request time: ").append(_statsHandler.getSuspendedTimeMin()).append("
\n"); - sb.append("Total suspended requests time: ").append(_statsHandler.getSuspendedTimeTotal()).append("
\n"); - - sb.append("

Responses:

\n"); - sb.append("1xx responses: ").append(_statsHandler.getResponses1xx()).append("
\n"); - sb.append("2xx responses: ").append(_statsHandler.getResponses2xx()).append("
\n"); - sb.append("3xx responses: ").append(_statsHandler.getResponses3xx()).append("
\n"); - sb.append("4xx responses: ").append(_statsHandler.getResponses4xx()).append("
\n"); - sb.append("5xx responses: ").append(_statsHandler.getResponses5xx()).append("
\n"); - sb.append("Bytes sent total: ").append(_statsHandler.getResponsesBytesTotal()).append("
\n"); + sb.append(_statsHandler.toStatsHTML()); sb.append("

Connections:

\n"); for (Connector connector : _connectors) diff --git a/jetty-util/pom.xml b/jetty-util/pom.xml index 864d26de187..02caedf9cc5 100644 --- a/jetty-util/pom.xml +++ b/jetty-util/pom.xml @@ -65,11 +65,6 @@ junit test - - javax.servlet - servlet-api - provided - org.slf4j slf4j-api diff --git a/test-jetty-webapp/src/main/java/com/acme/Dump.java b/test-jetty-webapp/src/main/java/com/acme/Dump.java index 98247193218..cd00632fc97 100644 --- a/test-jetty-webapp/src/main/java/com/acme/Dump.java +++ b/test-jetty-webapp/src/main/java/com/acme/Dump.java @@ -52,6 +52,7 @@ public class Dump extends HttpServlet { static boolean fixed; /* ------------------------------------------------------------ */ + @Override public void init(ServletConfig config) throws ServletException { super.init(config); @@ -131,26 +132,12 @@ public class Dump extends HttpServlet { e.printStackTrace(); } - Continuation continuation = ContinuationSupport.getContinuation(request,response); + Continuation continuation = ContinuationSupport.getContinuation(request); continuation.resume(); } }).start(); } - - if (request.getParameter("suspend")!=null) - { - try - { - Continuation continuation = ContinuationSupport.getContinuation(request,response); - continuation.setTimeout(Long.parseLong(request.getParameter("suspend"))); - continuation.suspend(); - } - catch(Exception e) - { - throw new ServletException(e); - } - } if (request.getParameter("complete")!=null) { @@ -183,6 +170,21 @@ public class Dump extends HttpServlet }).start(); } + if (request.getParameter("suspend")!=null && request.getAttribute("SUSPEND")!=Boolean.TRUE) + { + request.setAttribute("SUSPEND",Boolean.TRUE); + try + { + Continuation continuation = ContinuationSupport.getContinuation(request); + continuation.setTimeout(Long.parseLong(request.getParameter("suspend"))); + continuation.suspend(); + continuation.undispatch(); + } + catch(Exception e) + { + throw new ServletException(e); + } + } request.setAttribute("Dump", this); getServletContext().setAttribute("Dump",this); diff --git a/test-jetty-webapp/src/main/webapp/index.html b/test-jetty-webapp/src/main/webapp/index.html index 3748b1a793c..0d6734f4520 100644 --- a/test-jetty-webapp/src/main/webapp/index.html +++ b/test-jetty-webapp/src/main/webapp/index.html @@ -31,7 +31,7 @@ This is a test context that serves:
  • a Dispatcher Servlet
  • a Request Rewrite Servlet
  • a Transparent Proxy (to www.google.com)
  • -
  • a CGI script (unix only)
  • +
  • a Comet Chat
  • a Authentication