Hi,

Firstly, thank you for all the amazing work you do in CXF. I participate in
the Apache TomEE project, which includes CXF to enable TomEE to provide
REST and SOAP based services.

We've had a slightly unusual problem crop up that specifically affects
applications that expose Jakarta REST endpoints (through the included CXF
in TomEE), and include the Spring Framework in the application's
WEB-INF/lib.

The exception we see is:

java.lang.NoClassDefFoundError:
org/springframework/web/filter/ServerHttpObservationFilter
    at
org.apache.cxf.jaxrs.springmvc.SpringWebUtils.setHttpRequestURI(SpringWebUtils.java:51)
~[cxf-rt-frontend-jaxrs-4.0.4.jar:4.0.4]
    at
org.apache.cxf.jaxrs.utils.HttpUtils.setHttpRequestURI(HttpUtils.java:380)
~[cxf-rt-frontend-jaxrs-4.0.4.jar:4.0.4]
    at
org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.setExchangeProperties(JAXRSInInterceptor.java:246)
~[cxf-rt-frontend-jaxrs-4.0.4.jar:4.0.4]
    at
org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:183)
~[cxf-rt-frontend-jaxrs-4.0.4.jar:4.0.4]
    at
org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:78)
~[cxf-rt-frontend-jaxrs-4.0.4.jar:4.0.4]
    at
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
~[cxf-core-4.0.4.jar:4.0.4]
    at
org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
~[cxf-core-4.0.4.jar:4.0.4]
    at
org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:265)
~[cxf-rt-transports-http-4.0.4.jar:4.0.4]
    at
org.apache.openejb.server.cxf.rs.CxfRsHttpListener.doInvoke(CxfRsHttpListener.java:266)
~[openejb-cxf-rs-9.1.3.jar:9.1.3]
    at
org.apache.tomee.webservices.CXFJAXRSFilter.doFilter(CXFJAXRSFilter.java:99)
~[tomee-jaxrs-9.1.3.jar:9.1.3]
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
~[catalina.jar:10.0.27]
    at
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
~[tomcat-websocket.jar:10.0.27]
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
~[catalina.jar:10.0.27]
    at org.apache.openejb.server.httpd.EEFilter.doFilter(EEFilter.java:67)
~[openejb-http-9.1.3.jar:9.1.3]
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
~[catalina.jar:10.0.27]
    at
org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
~[spring-web-6.0.19.jar:6.0.19]
    at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
~[spring-web-6.0.19.jar:6.0.19]
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
~[catalina.jar:10.0.27]
    at
org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
~[spring-web-6.0.19.jar:6.0.19]
    at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
~[spring-web-6.0.19.jar:6.0.19]
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
~[catalina.jar:10.0.27]
    at
org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:124)
~[spring-boot-3.1.11.jar:3.1.11]
    at
org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:99)
~[spring-boot-3.1.11.jar:3.1.11]
    at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
~[spring-web-6.0.19.jar:6.0.19]
    at
org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:117)
~[spring-boot-3.1.11.jar:3.1.11]
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
~[catalina.jar:10.0.27]
    at
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
~[spring-web-6.0.19.jar:6.0.19]
    at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
~[spring-web-6.0.19.jar:6.0.19]
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
~[catalina.jar:10.0.27]
    at
io.smallrye.metrics.jaxrs.JaxRsMetricsServletFilter.doFilter(JaxRsMetricsServletFilter.java:53)
~[smallrye-metrics-4.0.0.jar:na]
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
~[catalina.jar:10.0.27]
    at org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:45)
~[tomee-catalina-9.1.3.jar:9.1.3]
    at
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
~[catalina.jar:10.0.27]
    at
org.apache.tomee.catalina.OpenEJBSecurityListener$RequestCapturer.invoke(OpenEJBSecurityListener.java:97)
~[tomee-catalina-9.1.3.jar:9.1.3]
    at
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
~[catalina.jar:10.0.27]
    at
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:356)
~[catalina.jar:10.0.27]
    at
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
~[tomcat-coyote.jar:10.0.27]
    at
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
~[tomcat-coyote.jar:10.0.27]
    at
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:870)
~[tomcat-coyote.jar:10.0.27]
    at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1762)
~[tomcat-coyote.jar:10.0.27]
    at
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
~[tomcat-coyote.jar:10.0.27]
    at
org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
~[tomcat-util.jar:10.0.27]
    at
org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
~[tomcat-util.jar:10.0.27]
    at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
~[tomcat-util.jar:10.0.27]
    at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: java.lang.ClassNotFoundException:
org.springframework.web.filter.ServerHttpObservationFilter
    at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
~[na:na]
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592)
~[na:na]
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
~[na:na]
    ... 58 common frames omitted

It looks like org.apache.cxf.jaxrs.springmvc.SpringWebUtils checks whether
Spring is present using the Thread Context Class Loader, which in the case
of TomEE includes the application's classes and libraries, and TomEE's
libraries (as a parent class loader).

The setHttpRequestURI() method, however, uses the Class Loader that loaded
org.apache.cxf.jaxrs.springmvc.SpringWebUtils (the parent class loader), so
the Spring Framework included in the application is not visible at this
point.

I would imagine that if this were deployed in Tomcat, and both CXF and
Spring were included in the application's WEB-INF/lib, this would not be an
issue.

We can mitigate this in TomEE by patching
org.apache.cxf.jaxrs.springmvc.SpringWebUtils like this:
https://github.com/apache/tomee/pull/1131/files - i.e. in
setHttpRequestURI() invoke Spring using reflection, loading the necessary
classes from the Thread Context Class Loader. This doesn't feel
particularly elegant, however, and I wonder if there's a better fix that
could be applied in CXF?

If you have any thoughts, and would be willing to accept a change to help
out with this, I'd be very happy to work on the change and provide a pull
request.

Many thanks

Jon

Reply via email to