Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedded cglib 3.2.5 not closing input streams that read class files [SPR-16267] #20814

Closed
spring-issuemaster opened this issue Dec 5, 2017 · 4 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

commented Dec 5, 2017

Gregg Yost opened SPR-16267 and commented

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: cglib/cglib#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.


Affects: 4.3.9, 4.3.13

Attachments:

Issue Links:

  • #20951 Upgrade to ASM 6.0 level (for CGLIB 3.2.6)

Referenced from: commits 4aeca67, b2ce98e

Backported to: 4.3.14

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Dec 5, 2017

Juergen Hoeller commented

We'll pick up CGLIB 3.2.6 once released.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 19, 2018

Gregg Yost commented

CGLIB 3.2.6 was just released today.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 20, 2018

Juergen Hoeller commented

Master is upgraded to CGLIB 3.2.6 now. I'll backport this to 4.3.x right away.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 22, 2018

Gregg Yost commented

Thank you very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.