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-projects-issues opened this issue Dec 5, 2017 · 4 comments
Assignees
Labels
in: core status: backported type: bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues 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:

Referenced from: commits 4aeca67, b2ce98e

Backported to: 4.3.14

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Dec 5, 2017

Juergen Hoeller commented

We'll pick up CGLIB 3.2.6 once released.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 19, 2018

Gregg Yost commented

CGLIB 3.2.6 was just released today.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues 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-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 22, 2018

Gregg Yost commented

Thank you very much.

@spring-projects-issues spring-projects-issues added type: bug status: backported in: core labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 5.0.3 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core status: backported type: bug
Projects
None yet
Development

No branches or pull requests

2 participants