Very cool Chris, thanks for the quick reaction! Simon out
On Fri, Mar 22, 2024 at 1:41 PM Christopher Schultz < ch...@christopherschultz.net> wrote: > Simon, > > On 3/21/24 12:39, Simon Niederberger wrote: > > Hi Chris > > > > Personally I'd go with > > > > XML_INPUT_FACTORY = > > XMLInputFactory.newFactory(XMLInputFactory.class.getName(), > > EncodingDetector.class.getClassLoader()); > > > > allowing me to place my own JAR in common/lib if I really want to (the > > only scenario I can think of is an edge-case where there's a bug in > > the JVM XMLInputFactory and no upgrade path is available, so I'd place > > my own JAR in common/lib). > > > >> I wonder if there really are any use-cases for applications wanting > Tomcat to specify the XMLInputFactory *to be used for JSP* > > > > That sounds pretty far-fetched. Our use case is simply that we deploy > > frequently (several times a day) on a Tomcat 10.1 installation, and > > have various reasons for not going down the Docker/container/FAT JAR > > path. So, making sure we don't have memory leaks is relevant. > > Done and done. > > Feel free to put the fix in yourself, or grab 10.1.x from GitHub, or wit > for Tomcat 10.1.21 to be released (10.1.20 has already been rolled and > the voting will end shortly, so you'll have to wait for the next release). > > -chris > > > On Thu, Mar 21, 2024 at 1:44 PM Christopher Schultz > > <ch...@christopherschultz.net> wrote: > >> > >> Simon, > >> > >> On 3/20/24 15:36, Simon Niederberger wrote: > >>>> What if you create an empty jaxp.properties file and make it > available to the common ClassLoader (e.g. in > lib/empty-jaxp.jar:/jaxp.properties) -- does that prevent the problem? > >> > > >>> I think that boils down to what I'm already doing with the system > >>> property, getting to the FactoryFinder before it uses > >>> findServiceProvider(). Haven't tried it, but I'm sure it would work. > >> > >> The difference is that Tomcat can ship it as a part of the distribution, > >> while we shouldn't really be setting system properties like that. It > >> might prevent, for example, applications using their own preferred > >> implementation. > >> > >>> imho, Tomcat's EncodingDetector should init with either > >>> > >>> XML_INPUT_FACTORY = XMLInputFactory.newDefaultFactory(); > >>> > >>> to just always use the JVM XMLInputFactory, or then > >>> > >>> XML_INPUT_FACTORY = > >>> XMLInputFactory.newFactory(XMLInputFactory.class.getName(), > >>> EncodingDetector.class.getClassLoader()); > >>> > >>> to honor it's own classloader (maybe > >>> EncodingDetector.class.getClassLoader() is the wrong approach, > >>> basically something getTomcatCommonClassloader()) > >> > >> Yeah, that's probably better than an empty properties file wrapped in a > >> JAR file wrapped in an enigma. > >> > >> We also have the option of using the JreMemoryLeakPreventionListener for > >> this. There are already some XML-related protections in there, though > >> this one is not specifically there. > >> > >> Do you have a preferred technique for fixing this? All of those > >> suggestions seem equally reasonable. I think I have a slight preference > >> for passing the JSP compiler's ClassLoader in to the factory method: > >> > >> XML_INPUT_FACTORY = > >> XMLInputFactory.newFactory(XMLInputFactory.class.getName(), > >> EncodingDetector.class.getClassLoader()); > >> > >> If you absolutey need to get your JSP compiler to have a custom > >> XMLInputFactory, you can copy the JSP compiler into your application and > >> it will use that ClassLoader instead. > >> > >>> What I just don't get is why there's so little online about others > >>> havingEncodingDetector similar issues. Spring + libs like CXF or > >>> jackson-dataformat-xml are common, both those libs have transitive > >>> dependencies on woodstox-core. Throw in ehcache, also common, and your > >>> webapp won't undeploy if it's the first webapp to load a JSP and thus > >>> clinit EncodingDetector. Maybe the public has just given up on clean > >>> undeploying. > >> > >> My guess is that most people don't really care too much about clean > >> shutdowns of their applications. They either bring their applications up > >> and down along with the whole JVM (either standalone or via Docker, > >> etc.) or they use an embedded Tomcat where their own application hosts > >> Tomcat and they start/stop that. > >> > >> Or they disable/ignore memory-leak detection log messages. *shrug* > >> > >> I wonder if there really are any use-cases for applications wanting > >> Tomcat to specify the XMLInputFactory *to be used for JSP*. I suspect > not. > >> > >> -chris > >> > >>> On Wed, Mar 20, 2024 at 7:01 PM Christopher Schultz > >>> <ch...@christopherschultz.net> wrote: > >>>> > >>>> Simon, > >>>> > >>>> On 3/20/24 09:59, Simon Niederberger wrote: > >>>>> The whole thing is caused by Maven dependencies which pull in > >>>>> com.fasterxml.woodstox:woodstox-core. The WstxInputFactory has a > >>>>> > >>>>> @ServiceProvider(XMLInputFactory.class) > >>>>> > >>>>> annotation, where ServiceProvider is > >>>>> org.ehcache.spi.service.ServiceProvider. I didn't manage to trace the > >>>>> key code section, but I do find that the > >>>>> javax.xml.stream.FactoryFinder then ends up finding WstxInputFactory > >>>>> as registered service provider. As WstxInputFactory is not on the > >>>>> common classpath (it's in WEB-INF/lib), I assume it's the webapp > >>>>> classloader which is used. Below is the stacktrace where > >>>>> EncodingDetector clinit happens (I defined a > >>>>> ch.want.funnel.FunnelApp$DelegatingXMLInputFactory to get > >>>>> stacktraces): > >>>>> > >>>>> Currently I'm setting > >>>>> > >>>>> System.setProperty("javax.xml.stream.XMLInputFactory", > >>>>> "com.sun.xml.internal.stream.XMLInputFactoryImpl"); > >>>>> to get a XMLInputFactory implementation which is on the common > >>>>> loader's classpath, so the webapp can be undeployed cleanly. > >>>> > >>>> So this works, right? > >>>> > >>>> What if you create an empty jaxp.properties file and make it available > >>>> to the common ClassLoader (e.g. in > lib/empty-jaxp.jar:/jaxp.properties) > >>>> -- does that prevent the problem? > >>>> > >>>> I'm wondering if Tomcat should simply ship with an empty > jaxp.properties > >>>> file to prevent this kind of thing from happening by default. If > someone > >>>> wants to bundle an XMLInputFactory into Tomcat's lib/ directory and > use > >>>> that, they could remove this file. > >>>> > >>>> BTW that's an impressive stack trace. ;) > >>>> > >>>> -chris > >>>> > >>>>> java.lang.RuntimeException: Stracktrace for tracking XMLInputFactory > creation > >>>>> at > ch.want.funnel.FunnelApp$DelegatingXMLInputFactory.<init>(FunnelApp.java:107) > >>>>> at > java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native > >>>>> Method) > >>>>> at > java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) > >>>>> at > java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) > >>>>> at > java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) > >>>>> at > java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) > >>>>> at > java.xml/javax.xml.stream.FactoryFinder.newInstance(FactoryFinder.java:190) > >>>>> at > java.xml/javax.xml.stream.FactoryFinder.newInstance(FactoryFinder.java:148) > >>>>> at > java.xml/javax.xml.stream.FactoryFinder.find(FactoryFinder.java:261) > >>>>> at > java.xml/javax.xml.stream.FactoryFinder.find(FactoryFinder.java:223) > >>>>> at > java.xml/javax.xml.stream.XMLInputFactory.newInstance(XMLInputFactory.java:166) > >>>>> at > org.apache.jasper.compiler.EncodingDetector.<clinit>(EncodingDetector.java:38) > >>>>> at > org.apache.jasper.compiler.ParserController.determineSyntaxAndEncoding(ParserController.java:324) > >>>>> at > org.apache.jasper.compiler.ParserController.doParse(ParserController.java:201) > >>>>> at > org.apache.jasper.compiler.ParserController.parseDirectives(ParserController.java:128) > >>>>> at > org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:207) > >>>>> at > org.apache.jasper.compiler.Compiler.compile(Compiler.java:396) > >>>>> at > org.apache.jasper.compiler.Compiler.compile(Compiler.java:372) > >>>>> at > org.apache.jasper.compiler.Compiler.compile(Compiler.java:356) > >>>>> at > org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:603) > >>>>> at > org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:396) > >>>>> at > org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:380) > >>>>> at > org.apache.jasper.servlet.JspServlet.service(JspServlet.java:328) > >>>>> at > jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) > >>>>> at > org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) > >>>>> at > org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) > >>>>> at > org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:91) > >>>>> at > org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227) > >>>>> at > org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:75) > >>>>> at > org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) > >>>>> at > org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186) > >>>>> at > org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) > >>>>> at > org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$3(HandlerMappingIntrospector.java:195) > >>>>> at > org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) > >>>>> at > org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) > >>>>> at > org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:225) > >>>>> at > org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352) > >>>>> at > org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:642) > >>>>> at > org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:408) > >>>>> at > org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:340) > >>>>> at > org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:277) > >>>>> at > org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequestDispatcher.forward(HeaderWriterFilter.java:170) > >>>>> at > org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:171) > >>>>> at > org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:314) > >>>>> at > org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1431) > >>>>> at > org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1167) > >>>>> at > org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1106) > >>>>> at > org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) > >>>>> at > org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) > >>>>> at > org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) > >>>>> at > jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) > >>>>> at > org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) > >>>>> at > jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.AbstractRequestLoggingFilter.doFilterInternal(AbstractRequestLoggingFilter.java:289) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) > >>>>> at > org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) > >>>>> at > org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) > >>>>> at > org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) > >>>>> at > org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227) > >>>>> at > org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) > >>>>> at > org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) > >>>>> at > org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) > >>>>> at > org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) > >>>>> at > org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) > >>>>> at > org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) > >>>>> at > org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) > >>>>> at > org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$3(HandlerMappingIntrospector.java:195) > >>>>> at > org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) > >>>>> at > org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) > >>>>> at > org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:225) > >>>>> at > org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352) > >>>>> at > org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:124) > >>>>> at > org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:99) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:117) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) > >>>>> at > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) > >>>>> at > org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) > >>>>> at > org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) > >>>>> at > org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) > >>>>> at > org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) > >>>>> at > org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) > >>>>> at > org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) > >>>>> at > org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:673) > >>>>> at > org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) > >>>>> at > org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) > >>>>> at > org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:431) > >>>>> at > org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) > >>>>> at > org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) > >>>>> at org.apache.tomcat.util.net > .NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) > >>>>> at org.apache.tomcat.util.net > .SocketProcessorBase.run(SocketProcessorBase.java:52) > >>>>> at > org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) > >>>>> at > org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) > >>>>> at > org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) > >>>>> at java.base/java.lang.Thread.run(Thread.java:840) > >>>>> > >>>>> > >>>>> Mühlegasse 18, 6340 Baar, Switzerland > >>>>> > >>>>> https://www.want.ch > >>>>> > >>>>> https://www.funnel.travel > >>>>> > >>>>> > >>>>> > >>>>> > >>>>> > >>>>> On Tue, Mar 19, 2024 at 12:31 PM Christopher Schultz > >>>>> <ch...@christopherschultz.net> wrote: > >>>>>> > >>>>>> Simon, > >>>>>> > >>>>>> On 3/18/24 15:17, Simon Niederberger wrote: > >>>>>>> I'm analyzing a memory leak reported by Tomcat, and have narrowed > it > >>>>>>> down to org.apache.jasper.compiler.EncodingDetector: > >>>>>>> > >>>>>>> private static final XMLInputFactory XML_INPUT_FACTORY; > >>>>>>> static { > >>>>>>> XML_INPUT_FACTORY = XMLInputFactory.newInstance(); > >>>>>>> } > >>>>>>> > >>>>>>> This class is called by webapp code on a GET request > >>>>>>> > >>>>>>> at > org.apache.jasper.compiler.EncodingDetector.<clinit>(EncodingDetector.java:38) > >>>>>>> at > org.apache.jasper.compiler.ParserController.determineSyntaxAndEncoding(ParserController.java:324) > >>>>>>> at > org.apache.jasper.compiler.ParserController.doParse(ParserController.java:201) > >>>>>>> at > org.apache.jasper.compiler.ParserController.parseDirectives(ParserController.java:128) > >>>>>>> at > org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:207) > >>>>>>> ... > >>>>>>> at > org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:396) > >>>>>>> at > org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:380) > >>>>>>> at > org.apache.jasper.servlet.JspServlet.service(JspServlet.java:328) > >>>>>>> at > jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) > >>>>>>> > >>>>>>> The EncodingDetector class, if not yet loaded, will be loaded in > the > >>>>>>> common classloader, then continue by loading the XMLInputFactory > using > >>>>>>> the webapp context, and might end up with a XMLInputFactory > >>>>>>> implementation from a webapp-provided JAR. If that happens, the > webapp > >>>>>>> can't undeploy. (In my case, woodstox WstxInputFactory registers > >>>>>>> itself as ServiceProvider for XMLInputFactory) > >>>>>>> > >>>>>>> For completeness: > javax.xml.stream.FactoryFinder.findServiceProvider() > >>>>>>> is called without classloader (cl = null), and has > >>>>>>> > >>>>>>> if (cl == null) { > >>>>>>> //the current thread's context class loader > >>>>>>> serviceLoader = ServiceLoader.load(type); > >>>>>>> } else { > >>>>>>> serviceLoader = ServiceLoader.load(type, cl); > >>>>>>> } > >>>>>>> > >>>>>>> I can't find anything online about memory leaks from > webapp-provided > >>>>>>> XMLInputFactory implementations, but this must be fairly common. > Is my > >>>>>>> understanding correct, or have I mis-configured something? (I'm > mainly > >>>>>>> wondering whether any XMLInputFactory-implementing JARs belong in > >>>>>>> tomcat/lib, but again I'm not finding anything online confirming > that) > >>>>>>> > >>>>>>> Tomcat 10.1.19 > >>>>>>> JVM 17.0.10+7-Ubuntu-120.04.1 > >>>>>>> Ubuntu 20.04.6 LTS > >>>>>> > >>>>>> I'm not sure how many web applications ship with an XMLInputSource, > but > >>>>>> they definitely do exist. I'm fairly sure most applications won't > set a > >>>>>> system property or ship with a stax.properties/jaxp.properties file > to > >>>>>> override the default implementation, but of course if it's possible, > >>>>>> someone will eventually do it. > >>>>>> > >>>>>> I'm curious: in your example, how are you declaring your > implementation > >>>>>> class, and which implementation are you using? > >>>>>> > >>>>>> Are you able to log in EncodingDetector.<clinit> what the value of > the > >>>>>> thread's context classloader is? I would expect that it's using the > >>>>>> common classloader, as you say, and that the implementation class > would > >>>>>> also be loaded using that same classloader. > >>>>>> > >>>>>> -chris > >>>>>> > >>>>>> > --------------------------------------------------------------------- > >>>>>> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > >>>>>> For additional commands, e-mail: users-h...@tomcat.apache.org > >>>>>> > >>>>> > >>>>> > >>>>> --------------------------------------------------------------------- > >>>>> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > >>>>> For additional commands, e-mail: users-h...@tomcat.apache.org > >>>>> > >>>> > >>>> > >>>> --------------------------------------------------------------------- > >>>> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > >>>> For additional commands, e-mail: users-h...@tomcat.apache.org > >>>> > >>> > >>> > >>> --------------------------------------------------------------------- > >>> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > >>> For additional commands, e-mail: users-h...@tomcat.apache.org > >>> > >> > >> > >> --------------------------------------------------------------------- > >> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > >> For additional commands, e-mail: users-h...@tomcat.apache.org > >> > > > > > > --------------------------------------------------------------------- > > To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > > For additional commands, e-mail: users-h...@tomcat.apache.org > > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org > For additional commands, e-mail: users-h...@tomcat.apache.org > >