-
Notifications
You must be signed in to change notification settings - Fork 41.2k
TomcatEmbeddedWebappClassLoader.getResources() returns 2 entries for a single resource in a fat war #9014
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
Comments
@wilkinsona Could this be related to the issues in #8299? |
I don't think so, but I also wouldn't rule it out. Another possibility is the recent changes to Tomcat's resource URLs: the |
The problem also occurs with Tomcat 8.5.4 which does not use the
And with Spring Boot 1.5.1 which does not contain the changes for #8299:
Lastly, 1.4.0 has the same problem:
In short, things have been this way for a while and, the different separator and URL scheme aside, haven't changed recently |
This is a variant of the problem described in #7449 and fixed in 5e79657. The first of the three scenarios that I listed above still occurs as, unlike Spring Framework's The approach taken in 5e79657 won't work here as it's a |
@wilkinsona: thanks for analysis. I am trying to understand it. What do you think the reproducer should do to detect duplicates? I still find it strange that a single physical resources results in multiple entries. This is highly unexpected. |
It should compare their equality. One easy way to do so (and this is what Spring Framework's
You need to expect it as, unless you have complete control over all of the ClassLoaders involved, there's no guarantee that the same physical resource won't be visible to more than one ClassLoader in the hierarchy. Here's a (slightly contrived) example: File foo = new File("foo");
foo.mkdirs();
File bar = new File(foo, "bar.txt");
bar.createNewFile();
URLClassLoader parent = new URLClassLoader(new URL[] {foo.toURI().toURL()}, null);
URLClassLoader child = new URLClassLoader(new URL[] {foo.toURI().toURL()}, parent);
Enumeration<URL> resources = child.getResources("bar.txt");
while(resources.hasMoreElements()) {
System.out.println(resources.nextElement());
} The resulting enumeration contains two entries, both for the exact same URL because it's on the class path of both class loaders in the hierarchy. |
@snicoll Would be great if you could fix this team. We have a couple of users complaining about it: |
@tombujok Unfortunately, there's no obvious solution so it may be a while before we have a fix. I believe it will only affect users building a war and executing with |
Here's a possible fix: diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedWebappClassLoader.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedWebappClassLoader.java
index a2b156001c..b3e72c0f14 100644
--- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedWebappClassLoader.java
+++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedWebappClassLoader.java
@@ -16,7 +16,10 @@
package org.springframework.boot.context.embedded.tomcat;
+import java.io.IOException;
import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
import org.apache.catalina.loader.WebappClassLoader;
import org.apache.commons.logging.Log;
@@ -24,7 +27,7 @@ import org.apache.commons.logging.LogFactory;
/**
* Extension of Tomcat's {@link WebappClassLoader} that does not consider the
- * {@link ClassLoader#getSystemClassLoader() system classloader}. This is required to to
+ * {@link ClassLoader#getSystemClassLoader() system classloader}. This is required to
* ensure that any custom context classloader is always used (as is the case with some
* executable archives).
*
@@ -44,6 +47,16 @@ public class TomcatEmbeddedWebappClassLoader extends WebappClassLoader {
}
@Override
+ public URL findResource(String name) {
+ return null;
+ }
+
+ @Override
+ public Enumeration<URL> findResources(String name) throws IOException {
+ return Collections.emptyEnumeration();
+ }
+
+ @Override
public synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class<?> result = findExistingLoadedClass(name); The theory behind the fix is that all With the change in place, the reproducer behaves as desired when run with This feels like a pretty big change to be making in 1.5.x. @markt-asf Is what I'm proposing completely daft? Is there anything that I've overlooked that we'd lose by making |
It does strike me as odd that |
Thanks, @markt-asf.
When you launch a war file with During start up of the application, Boot detects the need for an embedded servlet container and that Tomcat should be used. At this point, it creates an embedded
That's reassuring. Thank you.
It makes me worry too. I think we may be best making this change in a 2.0 milestone and seeing how it goes. If it works as hoped we can then consider backporting it to 1.5.x. |
Was this fix backported to 1.5.* ? I see the fix merged into v2.0.3.RELEASE only. |
No. We considered it to be too risky to include in 1.5. The fix is in 2.0 M3 and later. |
Is there any workaround for 1.5.X that I can use ? I have hit this bug but I am unable to upgrade to 2.0.X yet. |
Everything we know about the problem is in this issue. The best workaround that we know of is to use jar packaging. If that’s not an option because you are using JSPs, you could switch to Jetty. |
We've worked around this issue by including spring-boot 2.x class |
@patrickconant I'm glad that works for you, but it's not an approach I'd generally recommend people adopt. |
I understand. It's risky and sub-optimal (like anything labelled a "workaround"), and I'm certainly not offering to support the resulting miscreation. I just wanted to provide it as an option to anyone that stumbles across this problem who cannot upgrade to 2.x for whatever reason. |
Uh oh!
There was an error while loading. Please reload this page.
I have a Spring Boot 1.5.3 application, packaged as an executable WAR with embedded Tomcat.
When I call
tccl.getResources()
then I am getting 2 entries for a single physical resource.Here is a reproducer: https://github.com/jerrinot/booottest
When I build it & execute it:
and access http://localhost:8080/ then I see this in the sysout:
This appears as a bug - a single physical resource should not produce 2 entries with different URLs.
It's working fine when I change the packaging from WAR to JAR.
EDIT: Obviously the affected Spring Boot version is 1.5.3, not 1.5.6 :)
The text was updated successfully, but these errors were encountered: