1717import io .opentelemetry .javaagent .bootstrap .servlet .MappingResolver ;
1818import io .opentelemetry .javaagent .instrumentation .servlet .ServletRequestContext ;
1919import io .opentelemetry .javaagent .instrumentation .servlet .v3_0 .snippet .Servlet3SnippetInjectingResponseWrapper ;
20+ import javax .annotation .Nullable ;
2021import javax .servlet .Servlet ;
2122import javax .servlet .ServletRequest ;
2223import javax .servlet .ServletResponse ;
2324import javax .servlet .http .HttpServletRequest ;
2425import javax .servlet .http .HttpServletResponse ;
2526import net .bytebuddy .asm .Advice ;
27+ import net .bytebuddy .asm .Advice .AssignReturned ;
28+ import net .bytebuddy .asm .Advice .AssignReturned .ToArguments .ToArgument ;
2629import net .bytebuddy .implementation .bytecode .assign .Assigner ;
2730
2831@ SuppressWarnings ("unused" )
2932public class Servlet3Advice {
3033
34+ // TODO
35+ public static class AdviceScope {
36+ public CallDepth callDepth ;
37+ public ServletRequestContext <HttpServletRequest > requestContext ;
38+ public Context context ;
39+ public Scope scope ;
40+
41+ public AdviceScope (
42+ CallDepth callDepth ,
43+ HttpServletRequest request ,
44+ HttpServletResponse response ,
45+ Object servletOrFilter ) {
46+ this .callDepth = callDepth ;
47+ this .callDepth .getAndIncrement ();
48+
49+ Context currentContext = Java8BytecodeBridge .currentContext ();
50+ Context attachedContext = helper ().getServerContext (request );
51+ Context contextToUpdate ;
52+
53+ requestContext = new ServletRequestContext <>(request , servletOrFilter );
54+ if (attachedContext == null && helper ().shouldStart (currentContext , requestContext )) {
55+ context = helper ().start (currentContext , requestContext );
56+ helper ().setAsyncListenerResponse (context , response );
57+
58+ contextToUpdate = context ;
59+ } else if (attachedContext != null
60+ && helper ().needsRescoping (currentContext , attachedContext )) {
61+ // Given request already has a context associated with it.
62+ // see the needsRescoping() javadoc for more explanation
63+ contextToUpdate = attachedContext ;
64+ } else {
65+ // We are inside nested servlet/filter/app-server span, don't create new span
66+ contextToUpdate = currentContext ;
67+ }
68+
69+ // Update context with info from current request to ensure that server span gets the best
70+ // possible name.
71+ // In case server span was created by app server instrumentations calling updateContext
72+ // returns a new context that contains servlet context path that is used in other
73+ // instrumentations for naming server span.
74+ MappingResolver mappingResolver = Servlet3Singletons .getMappingResolver (servletOrFilter );
75+ boolean servlet = servletOrFilter instanceof Servlet ;
76+ contextToUpdate = helper ().updateContext (contextToUpdate , request , mappingResolver , servlet );
77+ scope = contextToUpdate .makeCurrent ();
78+
79+ if (context != null ) {
80+ // Only trigger response customizer once, so only if server span was created here
81+ HttpServerResponseCustomizerHolder .getCustomizer ()
82+ .customize (contextToUpdate , response , Servlet3Accessor .INSTANCE );
83+ }
84+ }
85+
86+ public void exit (
87+ @ Nullable Throwable throwable , HttpServletRequest request , HttpServletResponse response ) {
88+
89+ boolean topLevel = callDepth .decrementAndGet () == 0 ;
90+ helper ().end (requestContext , request , response , throwable , topLevel , context , scope );
91+ }
92+ }
93+
94+ @ AssignReturned .ToArguments ({
95+ @ ToArgument (value = 0 , index = 1 ),
96+ @ ToArgument (value = 1 , index = 2 )
97+ })
3198 @ Advice .OnMethodEnter (suppress = Throwable .class )
32- public static void onEnter (
99+ public static Object [] onEnter (
33100 @ Advice .This (typing = Assigner .Typing .DYNAMIC ) Object servletOrFilter ,
34- @ Advice .Argument (value = 0 , readOnly = false ) ServletRequest request ,
35- @ Advice .Argument (value = 1 , readOnly = false ) ServletResponse response ,
36- @ Advice .Local ("otelCallDepth" ) CallDepth callDepth ,
37- @ Advice .Local ("otelRequest" ) ServletRequestContext <HttpServletRequest > requestContext ,
38- @ Advice .Local ("otelContext" ) Context context ,
39- @ Advice .Local ("otelScope" ) Scope scope ) {
101+ @ Advice .Argument (0 ) ServletRequest request ,
102+ @ Advice .Argument (1 ) ServletResponse originalResponse ) {
103+
104+ ServletResponse response = originalResponse ;
40105
41106 if (!(request instanceof HttpServletRequest ) || !(response instanceof HttpServletResponse )) {
42- return ;
107+ return new Object [] { null , request , response } ;
43108 }
44- HttpServletRequest httpServletRequest = (HttpServletRequest ) request ;
45109
46110 String snippet = getSnippetInjectionHelper ().getSnippet ();
47111 if (!snippet .isEmpty ()
@@ -50,70 +114,27 @@ public static void onEnter(
50114 response =
51115 new Servlet3SnippetInjectingResponseWrapper ((HttpServletResponse ) response , snippet );
52116 }
53- callDepth = CallDepth .forClass (AppServerBridge .getCallDepthKey ());
54- callDepth .getAndIncrement ();
55-
56- Context currentContext = Java8BytecodeBridge .currentContext ();
57- Context attachedContext = helper ().getServerContext (httpServletRequest );
58- Context contextToUpdate ;
59-
60- requestContext = new ServletRequestContext <>(httpServletRequest , servletOrFilter );
61- if (attachedContext == null && helper ().shouldStart (currentContext , requestContext )) {
62- context = helper ().start (currentContext , requestContext );
63- helper ().setAsyncListenerResponse (context , (HttpServletResponse ) response );
64-
65- contextToUpdate = context ;
66- } else if (attachedContext != null
67- && helper ().needsRescoping (currentContext , attachedContext )) {
68- // Given request already has a context associated with it.
69- // see the needsRescoping() javadoc for more explanation
70- contextToUpdate = attachedContext ;
71- } else {
72- // We are inside nested servlet/filter/app-server span, don't create new span
73- contextToUpdate = currentContext ;
74- }
75-
76- // Update context with info from current request to ensure that server span gets the best
77- // possible name.
78- // In case server span was created by app server instrumentations calling updateContext
79- // returns a new context that contains servlet context path that is used in other
80- // instrumentations for naming server span.
81- MappingResolver mappingResolver = Servlet3Singletons .getMappingResolver (servletOrFilter );
82- boolean servlet = servletOrFilter instanceof Servlet ;
83- contextToUpdate =
84- helper ().updateContext (contextToUpdate , httpServletRequest , mappingResolver , servlet );
85- scope = contextToUpdate .makeCurrent ();
86-
87- if (context != null ) {
88- // Only trigger response customizer once, so only if server span was created here
89- HttpServerResponseCustomizerHolder .getCustomizer ()
90- .customize (contextToUpdate , (HttpServletResponse ) response , Servlet3Accessor .INSTANCE );
91- }
117+ AdviceScope adviceScope =
118+ new AdviceScope (
119+ CallDepth .forClass (AppServerBridge .getCallDepthKey ()),
120+ (HttpServletRequest ) request ,
121+ (HttpServletResponse ) response ,
122+ servletOrFilter );
123+ return new Object [] {adviceScope , request , response };
92124 }
93125
94126 @ Advice .OnMethodExit (onThrowable = Throwable .class , suppress = Throwable .class )
95127 public static void stopSpan (
96128 @ Advice .Argument (0 ) ServletRequest request ,
97129 @ Advice .Argument (1 ) ServletResponse response ,
98- @ Advice .Thrown Throwable throwable ,
99- @ Advice .Local ("otelCallDepth" ) CallDepth callDepth ,
100- @ Advice .Local ("otelRequest" ) ServletRequestContext <HttpServletRequest > requestContext ,
101- @ Advice .Local ("otelContext" ) Context context ,
102- @ Advice .Local ("otelScope" ) Scope scope ) {
103-
104- if (!(request instanceof HttpServletRequest ) || !(response instanceof HttpServletResponse )) {
130+ @ Advice .Thrown @ Nullable Throwable throwable ,
131+ @ Advice .Enter Object [] enterResult ) {
132+ AdviceScope adviceScope = (AdviceScope ) enterResult [0 ];
133+ if (adviceScope == null
134+ || !(request instanceof HttpServletRequest )
135+ || !(response instanceof HttpServletResponse )) {
105136 return ;
106137 }
107-
108- boolean topLevel = callDepth .decrementAndGet () == 0 ;
109- helper ()
110- .end (
111- requestContext ,
112- (HttpServletRequest ) request ,
113- (HttpServletResponse ) response ,
114- throwable ,
115- topLevel ,
116- context ,
117- scope );
138+ adviceScope .exit (throwable , (HttpServletRequest ) request , (HttpServletResponse ) response );
118139 }
119140}
0 commit comments