Spring-boot: Add support for loading META-INF/resources from jars referenced via a Class-Path manifest entry

Created on 14 Jun 2017  路  4Comments  路  Source: spring-projects/spring-boot

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.

enhancement

Most helpful comment

@wilkinsona For many users of Vaadin + Spring Boot this issue has not yet been resolved, simply adding dev-tools did not fix the issue for us. I'm therefore requesting to re-open this issue and to reconsider the provided solution.

Please have a look at https://vaadin.com/forum/thread/17079950/loading-static-vaadin-resources-when-classpath-is-too-long-on-windows for a recent account.

All 4 comments

We already have code to do this in DevTools that was added for #5127.

I'm not sure what the alternative is, but I'm not too keen on having the same logic in a place where it'll be used in production. Let's see what the rest of the team thinks.

The reporter mentioned IJ so I guess adding devtools should actually fix the issue isn't it?

Good point, @snicoll. I think that's the best option here.

@wilkinsona For many users of Vaadin + Spring Boot this issue has not yet been resolved, simply adding dev-tools did not fix the issue for us. I'm therefore requesting to re-open this issue and to reconsider the provided solution.

Please have a look at https://vaadin.com/forum/thread/17079950/loading-static-vaadin-resources-when-classpath-is-too-long-on-windows for a recent account.

Was this page helpful?
0 / 5 - 0 ratings