-
Notifications
You must be signed in to change notification settings - Fork 41.3k
Description
Spring Boot 1.4.7
Background:
Due to path length restrictions on Windows, IntelliJ has a "dynamic classloading" feature. When enabled, it creates a file called "classpath.jar" and puts the classpath of the application into the MANIFEST.MF file,
Problem description:
Spring Boot normally adds "resource jars", i.e. those containing a "META-INF/resources" folder to the Tomcat context, making static resources available to the Tomcat servlet engine. This is done here:
TomcatEmbeddedServletContainerFactory:
context.addLifecycleListener(new LifecycleListener() {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
TomcatResources.get(context)
.addResourceJars(getUrlsOfJarsWithMetaInfResources());
}
}
});
In AbstractEmbeddedServletContainerFactory.getUrlsOfJarsWithMetaInfResources(), we find this code:
ClassLoader classLoader = getClass().getClassLoader();
List<URL> staticResourceUrls = new ArrayList<URL>();
if (classLoader instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) classLoader).getURLs()) {
[...]
This works as long as the ClassLoader used was initialized with a specific classpath. If the classpath refers to a jar that has a Class-Path entry in its manifest, Spring Boot does not find those jars.
Workaround:
I solved this issue locally by extending the TomcatEmbeddedServletContainerFactory, overriding the getUrlsOfJarsWithMetaInfResources() so that it also considers the entries in the Class-Path attribute of the jar file's manifest (if it exists).
So - in addition to the URLs returned by URLClassLoader.getURLs(), we add these to the set:
private URL[] getJarUrlsFromManifests(ClassLoader cl) {
try {
Set<URL> urlSet = new LinkedHashSet<>();
URL url = cl.getResource("META-INF/MANIFEST.MF");
if (url != null) {
Manifest manifest = new Manifest(url.openStream());
String classPath = manifest.getMainAttributes().getValue("Class-Path");
if (classPath != null) {
for (String urlStr : classPath.split(" ")) {
try {
urlSet.add(new URL(urlStr));
} catch (MalformedURLException ex) {
throw new AssertionError();
}
}
}
}
return urlSet.toArray(new URL[urlSet.size()]);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
I think Spring Boot should support this too.