Prevent webapp class loader from finding resources

Previously, TomcatEmbeddedWebappClassLoader would find resources
in WEB-INF/classes. However, unlike standalone Tomcat, we know that in
a Boot app WEB-INF/classes is on the class path of the parent class
loader so the resources will be found when the parent is queried
(which happens as part of the normal search algortithm for both
getResource(String) and getResources(String)).

This commit overrides findResource(String) and findResources(String)
to return null and an empty enumeration respectively. This prevents
TomcatEmbeddedWebappClassLoader from finding resources in
WEB-INF/classes and returning war: URLs for them that duplicate the
jar: URLs that will be found when the parent is queried.

Closes gh-9014
pull/9829/merge
Andy Wilkinson 7 years ago
parent 00feae7e8a
commit ded7cea761

@ -16,7 +16,10 @@
package org.springframework.boot.web.embedded.tomcat; package org.springframework.boot.web.embedded.tomcat;
import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import org.apache.catalina.loader.WebappClassLoader; import org.apache.catalina.loader.WebappClassLoader;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -44,6 +47,16 @@ public class TomcatEmbeddedWebappClassLoader extends WebappClassLoader {
super(parent); super(parent);
} }
@Override
public URL findResource(String name) {
return null;
}
@Override
public Enumeration<URL> findResources(String name) throws IOException {
return Collections.emptyEnumeration();
}
@Override @Override
public synchronized Class<?> loadClass(String name, boolean resolve) public synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException { throws ClassNotFoundException {

@ -0,0 +1,119 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.web.embedded.tomcat;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoader;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.catalina.webresources.WarResourceSet;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.util.CollectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link TomcatEmbeddedWebappClassLoader}.
*
* @author Andy Wilkinson
*/
public class TomcatEmbeddedWebappClassLoaderTests {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Test
public void getResourceFindsResourceFromParentClassLoader() throws Exception {
File war = createWar();
withWebappClassLoader(war, (classLoader) -> {
assertThat(classLoader.getResource("test.txt"))
.isEqualTo(new URL(webInfClassesUrlString(war) + "test.txt"));
});
}
@Test
public void getResourcesOnlyFindsResourcesFromParentClassLoader() throws Exception {
File warFile = createWar();
withWebappClassLoader(warFile, (classLoader) -> {
List<URL> urls = new ArrayList<>();
CollectionUtils.toIterator(classLoader.getResources("test.txt"))
.forEachRemaining(urls::add);
assertThat(urls).containsExactly(
new URL(webInfClassesUrlString(warFile) + "test.txt"));
});
}
private void withWebappClassLoader(File war, ClassLoaderConsumer consumer)
throws Exception {
URLClassLoader parent = new URLClassLoader(
new URL[] { new URL(webInfClassesUrlString(war)) }, null);
try (WebappClassLoader classLoader = new TomcatEmbeddedWebappClassLoader(
parent)) {
StandardContext context = new StandardContext();
context.setName("test");
StandardRoot resources = new StandardRoot();
resources.setContext(context);
resources.addJarResources(
new WarResourceSet(resources, "/", war.getAbsolutePath()));
resources.start();
classLoader.setResources(resources);
classLoader.start();
consumer.accept(classLoader);
}
}
private String webInfClassesUrlString(File war) {
return "jar:file:" + war.getAbsolutePath() + "!/WEB-INF/classes/";
}
private File createWar() throws IOException, FileNotFoundException {
File warFile = this.temp.newFile("test.war");
try (JarOutputStream warOut = new JarOutputStream(
new FileOutputStream(warFile))) {
createEntries(warOut, "WEB-INF/", "WEB-INF/classes/",
"WEB-INF/classes/test.txt");
}
return warFile;
}
private void createEntries(JarOutputStream out, String... names) throws IOException {
for (String name : names) {
out.putNextEntry(new ZipEntry(name));
out.closeEntry();
}
}
private interface ClassLoaderConsumer {
void accept(ClassLoader classLoader) throws Exception;
}
}
Loading…
Cancel
Save