1616import java .lang .invoke .MethodHandles ;
1717import java .lang .invoke .MethodType ;
1818import java .util .ArrayList ;
19+ import java .util .HashMap ;
1920import java .util .List ;
21+ import java .util .Map ;
2022import java .util .Objects ;
2123import javax .annotation .Nullable ;
2224import javax .servlet .Filter ;
2628import javax .servlet .ServletRequest ;
2729import javax .servlet .ServletResponse ;
2830import javax .servlet .http .HttpServletRequest ;
31+ import javax .servlet .http .HttpServletRequestWrapper ;
2932import javax .servlet .http .HttpServletResponse ;
3033import org .springframework .core .Ordered ;
3134import org .springframework .web .servlet .HandlerExecutionChain ;
3235import org .springframework .web .servlet .HandlerMapping ;
3336import org .springframework .web .servlet .mvc .method .annotation .RequestMappingHandlerMapping ;
3437
3538public class OpenTelemetryHandlerMappingFilter implements Filter , Ordered {
36- private static final String PATH_ATTRIBUTE = getRequestPathAttribute ();
3739 private static final MethodHandle usesPathPatternsMh = getUsesPathPatternsMh ();
3840 private static final MethodHandle parseAndCacheMh = parseAndCacheMh ();
3941
4042 private final HttpServerRouteGetter <HttpServletRequest > serverSpanName =
4143 (context , request ) -> {
42- Object previousValue = null ;
43- if (this .parseRequestPath && PATH_ATTRIBUTE != null ) {
44- previousValue = request .getAttribute (PATH_ATTRIBUTE );
44+ if (this .parseRequestPath ) {
4545 // sets new value for PATH_ATTRIBUTE of request
4646 parseAndCache (request );
4747 }
48- try {
49- if (findMapping (request )) {
50- // Name the parent span based on the matching pattern
51- // Let the parent span resource name be set with the attribute set in findMapping.
52- return SpringWebMvcServerSpanNaming .SERVER_SPAN_NAME .get (context , request );
53- }
54- } finally {
55- // mimic spring DispatcherServlet and restore the previous value of PATH_ATTRIBUTE
56- if (this .parseRequestPath && PATH_ATTRIBUTE != null ) {
57- if (previousValue == null ) {
58- request .removeAttribute (PATH_ATTRIBUTE );
59- } else {
60- request .setAttribute (PATH_ATTRIBUTE , previousValue );
61- }
62- }
48+ if (findMapping (request )) {
49+ // Name the parent span based on the matching pattern
50+ // Let the parent span resource name be set with the attribute set in findMapping.
51+ return SpringWebMvcServerSpanNaming .SERVER_SPAN_NAME .get (context , request );
6352 }
53+
6454 return null ;
6555 };
6656
@@ -84,14 +74,39 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
8474 } finally {
8575 if (handlerMappings != null ) {
8676 Context context = Context .current ();
87- HttpServerRoute .update (context , CONTROLLER , serverSpanName , ( HttpServletRequest ) request );
77+ HttpServerRoute .update (context , CONTROLLER , serverSpanName , prepareRequest ( request ) );
8878 }
8979 }
9080 }
9181
9282 @ Override
9383 public void destroy () {}
9484
85+ private static HttpServletRequest prepareRequest (ServletRequest request ) {
86+ // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/10379
87+ // Finding the request handler modifies request attributes. We are wrapping the request to avoid
88+ // this.
89+ return new HttpServletRequestWrapper ((HttpServletRequest ) request ) {
90+ private final Map <String , Object > attributes = new HashMap <>();
91+
92+ @ Override
93+ public void setAttribute (String name , Object o ) {
94+ attributes .put (name , o );
95+ }
96+
97+ @ Override
98+ public Object getAttribute (String name ) {
99+ Object value = attributes .get (name );
100+ return value != null ? value : super .getAttribute (name );
101+ }
102+
103+ @ Override
104+ public void removeAttribute (String name ) {
105+ attributes .remove (name );
106+ }
107+ };
108+ }
109+
95110 /**
96111 * When a HandlerMapping matches a request, it sets HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE
97112 * as an attribute on the request. This attribute is read by SpringWebMvcDecorator.onRequest and
@@ -186,19 +201,4 @@ private static void parseAndCache(HttpServletRequest request) {
186201 throw new IllegalStateException (throwable );
187202 }
188203 }
189-
190- private static String getRequestPathAttribute () {
191- try {
192- Class <?> pathUtilsClass =
193- Class .forName ("org.springframework.web.util.ServletRequestPathUtils" );
194- return (String )
195- MethodHandles .lookup ()
196- .findStaticGetter (pathUtilsClass , "PATH_ATTRIBUTE" , String .class )
197- .invoke ();
198- } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException exception ) {
199- return null ;
200- } catch (Throwable throwable ) {
201- throw new IllegalStateException (throwable );
202- }
203- }
204204}
0 commit comments