lujiajing1126 opened a new issue #6966:
URL: https://github.com/apache/skywalking/issues/6966
- Why do you submit this issue?
- [x] Question or discussion
- [x] Bug
- [ ] Requirement
- [ ] Feature or performance improvement
___
### Question
Discussion the `java.lang.ClassCastException` problem reported in issues
#6554, #5817, #6648 and #6760.
And try to resolve this problem.
___
### Bug
- Which version of SkyWalking, OS, and JRE?
I suppose all the versions up to the latest Skywalking 8.5.0 are affected
- What happened?
In the `skywalking-api.log`, we may find,
```
ERROR 2021-05-18 17:40:02:384 http-nio-8080-exec-1 InstMethodsInter :
class[class com.example.classloaderdemo.controller.HealthController] before
method[health] intercept failure
java.lang.ClassCastException:
org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder
cannot be cast to
org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder
at
org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor.AbstractMethodInterceptor.beforeMethod(AbstractMethodInterceptor.java:100)
at
org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:76)
at
com.example.classloaderdemo.controller.HealthController.health(HealthController.java)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
at
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
at
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
at
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060)
at
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)
at
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
at
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at
org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at
org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at
org.apache.catalina.core.StandardHostValve.invoke$original$G32s1bTQ(StandardHostValve.java:143)
at
org.apache.catalina.core.StandardHostValve.invoke$original$G32s1bTQ$accessor$ku9vVTW5(StandardHostValve.java)
at
org.apache.catalina.core.StandardHostValve$auxiliary$7vA1kjbB.call(Unknown
Source)
at
org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:86)
at
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java)
at
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
at
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707)
at
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
```
As @zifeihan has concluded in
https://github.com/apache/skywalking/issues/5817#issuecomment-751446913, the
problem is easy to be understood. Just to take a glance of this story, the
simplest way to reproduce the problem is to run a spring boot application with
[`spring-boot-devtools`](https://docs.spring.io/spring-boot/docs/1.5.16.RELEASE/reference/html/using-boot-devtools.html)
in IDEA.
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
```
with a controller,
```java
package com.example.classloaderdemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HealthController {
@GetMapping(path = "/healthz")
public String health() {
return "OK";
}
}
```
If we use [`Arthas`](https://arthas.aliyun.com/doc/index.html) to examine
this particular class
`org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder`, we can
find the following results,
```
[arthas@22299]$ sc -d
org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder
class-info
org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder
code-source
name
org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name JavaxServletRequestHolder
modifier public
annotation
interfaces
org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder
super-class +-java.lang.Object
class-loader
+-org.apache.skywalking.apm.agent.core.plugin.loader.AgentClassLoader@6ba76699
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@3feba861
classLoaderHash 6ba76699
class-info
org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder
code-source
name
org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder
isInterface true
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name RequestHolder
modifier abstract,interface,public
annotation
interfaces
super-class
class-loader
+-org.apache.skywalking.apm.agent.core.plugin.loader.AgentClassLoader@77f6031b
+-org.springframework.boot.devtools.restart.classloader.RestartClassLoader@6bce6c13
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@3feba861
classLoaderHash 77f6031b
class-info
org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder
code-source
name
org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder
isInterface true
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name RequestHolder
modifier abstract,interface,public
annotation
interfaces
super-class
class-loader
+-org.apache.skywalking.apm.agent.core.plugin.loader.AgentClassLoader@6ba76699
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@3feba861
classLoaderHash 6ba76699
Affect(row-cnt:3) cost in 30 ms.
```
1.
`org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder`
together with its parent
`org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder` appear to
be loaded by the `AgentClassLoader@6ba76699`. This happened when the spring
internal class `org.springframework.web.method.HandlerMethod#getBean` was
enhanced since the `HandlerMethod` class was loaded by `AppClassLoader`.
2. Afterwards, the `RestController` annotation is captured. However, the
controller class `HealthController` is loaded by another classloader
`org.springframework.boot.devtools.restart.classloader.RestartClassLoader@6bce6c13`
which further causes the Skywalking agent to create a new instance of
AgentClassLoader, i.e. `AgentClassLoader@77f6031b`.
Finally, since `RequestHolder` sits in both two classloaders, they are not
the same one, thus not inter-changeable.
___
### Requirement or improvement
As a conclusion, the root-cause of the problem is the abstraction of
`RequestHolder` which **exists in the plugin** and will be potentially loaded
by different instances of `AgentClassLoader`.
But I suppose the abstraction can be fully removed since we only take
advantage of it in `AbstractMethodInterceptor`.
If so, I believe this issue can be resolved, since either
`javax.servlet.http.HttpServletRequest` or
`org.springframework.http.server.reactive.ServerHttpResponse` can be only
loaded by the same classloader due to the
parent delegation model, e.g. `AppClassLoader` or
[`org.springframework.boot.loader.LaunchedURLClassLoader`](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/loader/LaunchedURLClassLoader.html)
in spring boot execution environment.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]