2424import jakarta .servlet .ServletResponse ;
2525import jakarta .servlet .http .HttpServletRequest ;
2626import jakarta .servlet .http .HttpServletResponse ;
27+ import javax .annotation .Nullable ;
2728import net .bytebuddy .asm .Advice ;
29+ import net .bytebuddy .asm .Advice .AssignReturned ;
30+ import net .bytebuddy .asm .Advice .AssignReturned .ToArguments .ToArgument ;
2831import net .bytebuddy .implementation .bytecode .assign .Assigner ;
2932
3033@ SuppressWarnings ("unused" )
3134public class JakartaServletServiceAdvice {
3235
36+ public static class AdviceScope {
37+ private final CallDepth callDepth ;
38+ private final ServletRequestContext <HttpServletRequest > requestContext ;
39+ private final Context context ;
40+ private final Scope scope ;
41+
42+ public AdviceScope (
43+ CallDepth callDepth ,
44+ Object servletOrFilter ,
45+ HttpServletRequest request ,
46+ ServletResponse response ) {
47+ this .callDepth = callDepth ;
48+ this .callDepth .getAndIncrement ();
49+
50+ Context currentContext = Java8BytecodeBridge .currentContext ();
51+ Context attachedContext = helper ().getServerContext (request );
52+ Context contextToUpdate ;
53+
54+ requestContext = new ServletRequestContext <>(request , servletOrFilter );
55+ if (attachedContext == null && helper ().shouldStart (currentContext , requestContext )) {
56+ context = helper ().start (currentContext , requestContext );
57+ helper ().setAsyncListenerResponse (context , (HttpServletResponse ) response );
58+
59+ contextToUpdate = context ;
60+ } else if (attachedContext != null
61+ && helper ().needsRescoping (currentContext , attachedContext )) {
62+ // Given request already has a context associated with it.
63+ // see the needsRescoping() javadoc for more explanation
64+ contextToUpdate = attachedContext ;
65+ context = null ;
66+ } else {
67+ // We are inside nested servlet/filter/app-server span, don't create new span
68+ contextToUpdate = currentContext ;
69+ context = null ;
70+ }
71+
72+ // Update context with info from current request to ensure that server span gets the best
73+ // possible name.
74+ // In case server span was created by app server instrumentations calling updateContext
75+ // returns a new context that contains servlet context path that is used in other
76+ // instrumentations for naming server span.
77+ MappingResolver mappingResolver = Servlet5Singletons .getMappingResolver (servletOrFilter );
78+ boolean servlet = servletOrFilter instanceof Servlet ;
79+ contextToUpdate = helper ().updateContext (contextToUpdate , request , mappingResolver , servlet );
80+ scope = contextToUpdate .makeCurrent ();
81+
82+ if (context != null ) {
83+ // Only trigger response customizer once, so only if server span was created here
84+ HttpServerResponseCustomizerHolder .getCustomizer ()
85+ .customize (contextToUpdate , (HttpServletResponse ) response , Servlet5Accessor .INSTANCE );
86+ }
87+ }
88+
89+ public void exit (
90+ HttpServletRequest request , HttpServletResponse response , @ Nullable Throwable throwable ) {
91+ boolean topLevel = callDepth .decrementAndGet () == 0 ;
92+ helper ().end (requestContext , request , response , throwable , topLevel , context , scope );
93+ }
94+ }
95+
96+ @ AssignReturned .ToArguments ({
97+ @ ToArgument (value = 0 , index = 1 ),
98+ @ ToArgument (value = 1 , index = 2 )
99+ })
33100 @ Advice .OnMethodEnter (suppress = Throwable .class )
34- public static void onEnter (
101+ public static Object [] onEnter (
35102 @ Advice .This (typing = Assigner .Typing .DYNAMIC ) Object servletOrFilter ,
36- @ Advice .Argument (value = 0 , readOnly = false ) ServletRequest request ,
37- @ Advice .Argument (value = 1 , readOnly = false ) ServletResponse response ,
38- @ Advice .Local ("otelCallDepth" ) CallDepth callDepth ,
39- @ Advice .Local ("otelRequest" ) ServletRequestContext <HttpServletRequest > requestContext ,
40- @ Advice .Local ("otelContext" ) Context context ,
41- @ Advice .Local ("otelScope" ) Scope scope ) {
103+ @ Advice .Argument (0 ) ServletRequest request ,
104+ @ Advice .Argument (1 ) ServletResponse originalResponse ) {
105+
106+ ServletResponse response = originalResponse ;
42107
43108 if (!(request instanceof HttpServletRequest ) || !(response instanceof HttpServletResponse )) {
44- return ;
109+ return new Object [] { null , request , response } ;
45110 }
46111 HttpServletRequest httpServletRequest = (HttpServletRequest ) request ;
47112
@@ -53,71 +118,31 @@ public static void onEnter(
53118 new Servlet5SnippetInjectingResponseWrapper ((HttpServletResponse ) response , snippet );
54119 }
55120
56- callDepth = CallDepth .forClass (AppServerBridge .getCallDepthKey ());
57- callDepth .getAndIncrement ();
58-
59- Context currentContext = Java8BytecodeBridge .currentContext ();
60- Context attachedContext = helper ().getServerContext (httpServletRequest );
61- Context contextToUpdate ;
62-
63- requestContext = new ServletRequestContext <>(httpServletRequest , servletOrFilter );
64- if (attachedContext == null && helper ().shouldStart (currentContext , requestContext )) {
65- context = helper ().start (currentContext , requestContext );
66- helper ().setAsyncListenerResponse (context , (HttpServletResponse ) response );
67-
68- contextToUpdate = context ;
69- } else if (attachedContext != null
70- && helper ().needsRescoping (currentContext , attachedContext )) {
71- // Given request already has a context associated with it.
72- // see the needsRescoping() javadoc for more explanation
73- contextToUpdate = attachedContext ;
74- } else {
75- // We are inside nested servlet/filter/app-server span, don't create new span
76- contextToUpdate = currentContext ;
77- }
121+ AdviceScope adviceScope =
122+ new AdviceScope (
123+ CallDepth .forClass (AppServerBridge .getCallDepthKey ()),
124+ servletOrFilter ,
125+ (HttpServletRequest ) request ,
126+ response );
78127
79- // Update context with info from current request to ensure that server span gets the best
80- // possible name.
81- // In case server span was created by app server instrumentations calling updateContext
82- // returns a new context that contains servlet context path that is used in other
83- // instrumentations for naming server span.
84- MappingResolver mappingResolver = Servlet5Singletons .getMappingResolver (servletOrFilter );
85- boolean servlet = servletOrFilter instanceof Servlet ;
86- contextToUpdate =
87- helper ().updateContext (contextToUpdate , httpServletRequest , mappingResolver , servlet );
88- scope = contextToUpdate .makeCurrent ();
89-
90- if (context != null ) {
91- // Only trigger response customizer once, so only if server span was created here
92- HttpServerResponseCustomizerHolder .getCustomizer ()
93- .customize (contextToUpdate , (HttpServletResponse ) response , Servlet5Accessor .INSTANCE );
94- }
128+ return new Object [] {adviceScope , request , response };
95129 }
96130
97131 @ Advice .OnMethodExit (onThrowable = Throwable .class , suppress = Throwable .class )
98132 public static void stopSpan (
99133 @ Advice .Argument (0 ) ServletRequest request ,
100134 @ Advice .Argument (1 ) ServletResponse response ,
101135 @ Advice .Thrown Throwable throwable ,
102- @ Advice .Local ("otelCallDepth" ) CallDepth callDepth ,
103- @ Advice .Local ("otelRequest" ) ServletRequestContext <HttpServletRequest > requestContext ,
104- @ Advice .Local ("otelContext" ) Context context ,
105- @ Advice .Local ("otelScope" ) Scope scope ) {
136+ @ Advice .Enter Object [] enterResult ) {
106137
107- if (!(request instanceof HttpServletRequest ) || !(response instanceof HttpServletResponse )) {
138+ AdviceScope adviceScope = (AdviceScope ) enterResult [0 ];
139+
140+ if (adviceScope == null
141+ || !(request instanceof HttpServletRequest )
142+ || !(response instanceof HttpServletResponse )) {
108143 return ;
109144 }
110145
111- boolean topLevel = callDepth .decrementAndGet () == 0 ;
112-
113- helper ()
114- .end (
115- requestContext ,
116- (HttpServletRequest ) request ,
117- (HttpServletResponse ) response ,
118- throwable ,
119- topLevel ,
120- context ,
121- scope );
146+ adviceScope .exit ((HttpServletRequest ) request , (HttpServletResponse ) response , throwable );
122147 }
123148}
0 commit comments