Skip to content

Static resources from META-INF/resources not made available to Tomcat when packaged as "fat jar" #9525

Closed
@torerefsnes

Description

@torerefsnes

Spring Boot 1.5.4

Scenario:
A Spring Boot application is packaged as a "fat jar". The jar then has the following structure:

BOOT-INF
  classes
    [...]
  lib
    comp1.jar
    [...]
META-INF
  resources
     index.jsp
    [...]
org
  springframework
    [...]

A number of static resources to be served by Tomcat are included in the META-INF/resources folder.

I find that the static resources are not added to the Tomcat servlet context, and cannot be found when run in "fat jar" mode. When not run as a "fat jar", it works.

Cause:
The reason for this is the way AbstractEmbeddedServletContainerFactory.getUrlsOfJarsWithMetaInfResources() works:

ClassLoader classLoader = getClass().getClassLoader();
List<URL> staticResourceUrls = new ArrayList<URL>();
if (classLoader instanceof URLClassLoader) {
	for (URL url : ((URLClassLoader) classLoader).getURLs()) {
[...]

The classloader hierarchy (with the results returned by getURLs()) is as follows when the application in run as a "fat jar":

sun.misc.Launcher$ExtClassLoader:
  [...] misc system jars

sun.misc.Launcher$AppClassLoader:
  **URL:file:/path/to/fatjar.jar**

org.springframework.boot.loader.LaunchedURLClassLoader: 
  URL:jar:file:/path/to/fatjar.jar!/BOOT-INF/classes!/
  URL:jar:file:/path/to/fatjar.jar!/BOOT-INF/lib!/comp1.jar!/

getClass() returns the LaunchedURLClassLoader, which only includes its embedded jars in getURLs(). Therefore, none of the resources in the main jar are found - only those embedded in the "fat jar". It works when not run as a fat jar, since the AppClassLoader returns all the URLs and paths to resources.

Suggested solution:
A solution that I have tested OK is to traverse the parent ClassLoaders too, scanning those for jars containing a META-INF/resources folder:

private URL[] getURLs(URLClassLoader classLoader) {
    Set<URL> urlSet = new LinkedHashSet<>();

    ClassLoader c = classLoader;

    while (c != null) {
        if (c instanceof URLClassLoader) {
            Collections.addAll(urlSet, ((URLClassLoader) c).getURLs());
        }

        c = c.getParent();
    }

    return urlSet.toArray(new URL[urlSet.size()]);
}
    ClassLoader classLoader = getClass().getClassLoader();
    List<URL> staticResourceUrls = new ArrayList<>();
    if (classLoader instanceof URLClassLoader) {
->       for (URL url : getURLs((URLClassLoader) classLoader)) {

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions