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

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

Closed
torerefsnes opened this issue Jun 15, 2017 · 1 comment
Labels
status: duplicate A duplicate of another issue

Comments

@torerefsnes
Copy link

torerefsnes commented Jun 15, 2017

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)) {
@wilkinsona
Copy link
Member

Duplicates #8324

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue
Projects
None yet
Development

No branches or pull requests

2 participants