11package io .dropwizard .metrics .jetty12 .ee10 ;
22
3+ import com .codahale .metrics .Meter ;
34import com .codahale .metrics .MetricRegistry ;
45import com .codahale .metrics .annotation .ResponseMeteredLevel ;
56import io .dropwizard .metrics .jetty12 .AbstractInstrumentedHandler ;
67import jakarta .servlet .AsyncEvent ;
78import jakarta .servlet .AsyncListener ;
8- import org .eclipse .jetty .ee10 .servlet .AsyncContextState ;
9+ import jakarta .servlet .ServletRequest ;
10+ import jakarta .servlet .ServletRequestEvent ;
11+ import jakarta .servlet .ServletRequestListener ;
912import org .eclipse .jetty .ee10 .servlet .ServletApiRequest ;
10- import org .eclipse .jetty .ee10 .servlet .ServletApiResponse ;
1113import org .eclipse .jetty .ee10 .servlet .ServletChannelState ;
14+ import org .eclipse .jetty .ee10 .servlet .ServletContextHandler ;
1215import org .eclipse .jetty .ee10 .servlet .ServletContextRequest ;
1316import org .eclipse .jetty .server .Handler ;
1417import org .eclipse .jetty .server .Request ;
1518import org .eclipse .jetty .server .Response ;
16- import org .eclipse .jetty .util .Callback ;
1719
1820import java .io .IOException ;
19- import java .util .concurrent .TimeUnit ;
20-
21- import static com .codahale .metrics .annotation .ResponseMeteredLevel .COARSE ;
2221
2322/**
2423 * A Jetty {@link Handler} which records various metrics about an underlying {@link Handler}
2827 * {@link org.eclipse.jetty.ee10.servlet.ServletContextHandler#insertHandler(Singleton)}.
2928 */
3029public class InstrumentedEE10Handler extends AbstractInstrumentedHandler {
31- private AsyncListener listener ;
30+ private AsyncDispatchesAwareServletRequestListener asyncDispatchesAwareServletRequestListener ;
3231
3332 /**
3433 * Create a new instrumented handler using a given metrics registry.
3534 *
3635 * @param registry the registry for the metrics
3736 */
3837 public InstrumentedEE10Handler (MetricRegistry registry ) {
39- super (registry , null );
38+ super (registry );
4039 }
4140
4241 /**
@@ -46,7 +45,7 @@ public InstrumentedEE10Handler(MetricRegistry registry) {
4645 * @param prefix the prefix to use for the metrics names
4746 */
4847 public InstrumentedEE10Handler (MetricRegistry registry , String prefix ) {
49- super (registry , prefix , COARSE );
48+ super (registry , prefix );
5049 }
5150
5251 /**
@@ -63,8 +62,7 @@ public InstrumentedEE10Handler(MetricRegistry registry, String prefix, ResponseM
6362 @ Override
6463 protected void doStart () throws Exception {
6564 super .doStart ();
66-
67- this .listener = new AsyncAttachingListener ();
65+ asyncDispatchesAwareServletRequestListener = new AsyncDispatchesAwareServletRequestListener (getAsyncDispatches ());
6866 }
6967
7068 @ Override
@@ -73,104 +71,84 @@ protected void doStop() throws Exception {
7371 }
7472
7573 @ Override
76- public boolean handle (Request request , Response response , Callback callback ) throws Exception {
74+ protected void setupServletListeners (Request request , Response response ) {
7775 ServletContextRequest servletContextRequest = Request .as (request , ServletContextRequest .class );
78-
79- // only handle servlet requests with the InstrumentedHandler
80- // because it depends on the ServletRequestState
8176 if (servletContextRequest == null ) {
82- return super .handle (request , response , callback );
83- }
84-
85- activeDispatches .inc ();
86-
87- final long start ;
88- final ServletChannelState state = servletContextRequest .getServletRequestState ();
89- if (state .isInitial ()) {
90- // new request
91- activeRequests .inc ();
92- start = Request .getTimeStamp (request );
93- state .addListener (listener );
94- } else {
95- // resumed request
96- start = System .currentTimeMillis ();
97- activeSuspended .dec ();
98- if (state .getState () == ServletChannelState .State .HANDLING ) {
99- asyncDispatches .mark ();
100- }
77+ return ;
10178 }
10279
103- boolean handled = false ;
80+ ServletChannelState servletChannelState = servletContextRequest .getServletRequestState ();
81+ // the ServletChannelState gets recycled after handling, so add a new listener for every request
82+ servletChannelState .addListener (new InstrumentedAsyncListener (getAsyncTimeouts ()));
10483
105- try {
106- handled = super .handle (request , response , callback );
107- } finally {
108- final long now = System .currentTimeMillis ();
109- final long dispatched = now - start ;
84+ ServletContextHandler servletContextHandler = servletContextRequest .getServletContextHandler ();
85+ // addEventListener checks for duplicates, so we can try to add the listener for every request
86+ servletContextHandler .addEventListener (asyncDispatchesAwareServletRequestListener );
87+ }
11088
111- activeDispatches .dec ();
112- dispatches .update (dispatched , TimeUnit .MILLISECONDS );
89+ @ Override
90+ protected boolean isSuspended (Request request , Response response ) {
91+ ServletContextRequest servletContextRequest = Request .as (request , ServletContextRequest .class );
92+ if (servletContextRequest == null ) {
93+ return false ;
94+ }
11395
114- if (state .isSuspended ()) {
115- activeSuspended .inc ();
116- } else if (state .isInitial ()) {
117- updateResponses (request , response , start , handled );
118- }
119- // else onCompletion will handle it.
96+ ServletChannelState servletChannelState = servletContextRequest .getServletRequestState ();
97+ if (servletChannelState == null ) {
98+ return false ;
12099 }
121100
122- return handled ;
101+ return servletChannelState . isSuspended () ;
123102 }
124103
125- private class AsyncAttachingListener implements AsyncListener {
126-
127- @ Override
128- public void onTimeout (AsyncEvent event ) throws IOException {}
104+ private static class AsyncDispatchesAwareServletRequestListener implements ServletRequestListener {
105+ private final Meter asyncDispatches ;
129106
130- @ Override
131- public void onStartAsync (AsyncEvent event ) throws IOException {
132- event .getAsyncContext ().addListener (new InstrumentedAsyncListener ());
107+ private AsyncDispatchesAwareServletRequestListener (Meter asyncDispatches ) {
108+ this .asyncDispatches = asyncDispatches ;
133109 }
134110
135111 @ Override
136- public void onError (AsyncEvent event ) throws IOException {}
112+ public void requestInitialized (ServletRequestEvent sre ) {
113+ ServletRequest servletRequest = sre .getServletRequest ();
114+ if (!(servletRequest instanceof ServletApiRequest )) {
115+ return ;
116+ }
137117
138- @ Override
139- public void onComplete (AsyncEvent event ) throws IOException {}
118+ ServletApiRequest servletApiRequest = (ServletApiRequest ) servletRequest ;
119+
120+ ServletContextHandler .ServletRequestInfo servletRequestInfo = servletApiRequest .getServletRequestInfo ();
121+
122+ ServletChannelState servletChannelState = servletRequestInfo .getServletRequestState ();
123+
124+ // if the request isn't 'initial', the request was re-dispatched
125+ if (servletChannelState .isAsync () && !servletChannelState .isInitial ()) {
126+ asyncDispatches .mark ();
127+ }
128+ }
140129 }
141130
142- private class InstrumentedAsyncListener implements AsyncListener {
143- private final long startTime ;
131+ private static class InstrumentedAsyncListener implements AsyncListener {
132+ private final Meter asyncTimeouts ;
144133
145- InstrumentedAsyncListener () {
146- this .startTime = System . currentTimeMillis () ;
134+ private InstrumentedAsyncListener (Meter asyncTimeouts ) {
135+ this .asyncTimeouts = asyncTimeouts ;
147136 }
148137
149138 @ Override
150- public void onTimeout (AsyncEvent event ) throws IOException {
151- asyncTimeouts .mark ();
152- }
139+ public void onComplete (AsyncEvent event ) throws IOException {}
153140
154141 @ Override
155- public void onStartAsync (AsyncEvent event ) throws IOException {
142+ public void onTimeout (AsyncEvent event ) throws IOException {
143+ asyncTimeouts .mark ();
156144 }
157145
158146 @ Override
159- public void onError (AsyncEvent event ) throws IOException {
160- }
147+ public void onError (AsyncEvent event ) throws IOException {}
161148
162149 @ Override
163- public void onComplete (AsyncEvent event ) throws IOException {
164- final AsyncContextState state = (AsyncContextState ) event .getAsyncContext ();
165- final ServletApiRequest request = (ServletApiRequest ) state .getRequest ();
166- final ServletApiResponse response = (ServletApiResponse ) state .getResponse ();
167- updateResponses (request .getRequest (), response .getResponse (), startTime , true );
168-
169- final ServletContextRequest servletContextRequest = Request .as (request .getRequest (), ServletContextRequest .class );
170- final ServletChannelState servletRequestState = servletContextRequest .getServletRequestState ();
171- if (!servletRequestState .isSuspended ()) {
172- activeSuspended .dec ();
173- }
150+ public void onStartAsync (AsyncEvent event ) throws IOException {
151+ event .getAsyncContext ().addListener (this );
174152 }
175153 }
176154}
0 commit comments