Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-16267

Embedded cglib 3.2.5 not closing input streams that read class files

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Complete
    • Affects Version/s: 4.3.9, 4.3.13
    • Fix Version/s: 4.3.14, 5.0.3
    • Component/s: Core
    • Labels:
      None
    • Last commented by a User:
      true

      Description

      cglib 3.2.5, which is currently the latest released cglib version and is the version that Spring embeds in spring-core, has a bug where it calls ClassLoader getResourceAsStream but never closes the stream. I've reported this cglib problem here: https://github.com/cglib/cglib/issues/115

      The bug is in cglib.proxy.BridgeMethodResolver's resolveAll method. Spring can arrive in that code when a Spring configuration class requires synthetic bridge methods as part of Java's type erasure for generics. For example, I'm attaching an example to this issue where I have an ApplicationListener class that listens for ContextRefreshedEvents using a generic, like this:

      @Configuration
      public class ListenerSpringConfig implements ApplicationListener<ContextRefreshedEvent>
      {
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event)
        {
        }
      }
      

      At web application startup, the presence of that class results in Spring invoking the problematic BridgeMethodResolver code via the path shown in this stack trace:

      java.lang.Thread.State: RUNNABLE
      	  at org.springframework.cglib.proxy.BridgeMethodResolver.resolveAll(BridgeMethodResolver.java:60)
      	  at org.springframework.cglib.proxy.Enhancer.emitMethods(Enhancer.java:1132)
      	  at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:630)
      	  at org.springframework.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33)
      	  at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
      	  at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanFactoryAwareGeneratorStrategy.generate(ConfigurationClassEnhancer.java:252)
      	  at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:329)
      	  at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:492)
      	  at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:93)
      	  at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:91)
      	  at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
      	  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      	  at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
      	  at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
      	  at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116)
      	  at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
      	  at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
      	  at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:337)
      	  at org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(ConfigurationClassEnhancer.java:138)
      	  at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:110)
      	  at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:393)
      	  at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:249)
      	  at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:283)
      	  at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:127)
      	  at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687)
      	  at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525)
      	  - locked <0x1122> (a java.lang.Object)
      	  at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
      	  at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
      	  at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
      	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
      	  at org.springframework.boot.web.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:154)
      	  at org.springframework.boot.web.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:134)
      	  at org.springframework.boot.web.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:87)
      	  at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169)
      	  at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5196)
      	  - locked <0x1112> (a org.apache.catalina.core.StandardContext)
      	  at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
      	  at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:752)
      	  at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:728)
      	  at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
      	  at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:988)
      	  at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1860)
      	  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
      	  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      	  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
      	  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
      	  at java.lang.Thread.run(Thread.java:748)
      

      The serious consequence of this bug shows up when my web application is deployed to Tomcat 8.5 on Windows, Tomcat refuses to undeploy the web application – I have to shut down the entire Tomcat server. This happens because Tomcat's classloader keeps a Windows file lock on jar files when someone has an open input stream on one of the classes in the jar file (such as when opened by classLoader.getResourceAsStream in cglib's BridgeMethodResolver). This is a substantial problem for users of my web application, since they cannot upgrade it without shutting down Tomcat, which often means that users get kicked out of all other web applications that Tomcat is hosting.

      To reproduce the problem, unpack the attached spring-locked-file.zip, and run "mvn package" in the top-level directory (use Java 8). It creates two thing: a separate jar file (locked-jar-1.0-SNAPSHOT.jar, built in the "jar" subproject) that contains the ListenerSpringConfig class shown above, and a war file (built in the "war" subproject) that contains that jar file in its WEB-INF/lib. The "mvn package" will result in a war file named locked-file-1.0-SNAPSHOT.war in war/target. Deploy that war file in Tomcat 8.5 on Windows. Wait for the web application to finish deploying, then undeploy the web application. The application will not undeploy completely, and the deployed war directory will still contain WEB-INF/lib/locked-jar-1.0-SNAPSHOT after the undeployment attempt, because Tomcat still has the jar file open because of the never-closed InputStream, and Windows does not allow an open file to be deleted. I tried with both Tomcat 8.5.14 and the most-recent 8.5.23, and the problem occurs with both.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              juergen.hoeller Juergen Hoeller
              Reporter:
              yogregg Gregg Yost
              Last updater:
              Spring Issues Spring Issues
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:
                Days since last comment:
                3 years, 37 weeks, 6 days ago