spring-projects / spring-projects/spring-boot

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

Open
#9,513 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

type: enhancement
Dominant language
Java
Stars
81.5k
Forks
42.7k
Avg merge
2d 4h
Merged PRs (30d)
65

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with AbstractEmbeddedServletContainerFactory.getUrlsOfJarsWithMetaInfResources() and the resource-jar setup in TomcatEmbeddedServletContainerFactory. Read how URLClassLoader.getURLs() is currently used, then verify that jars named by a Class-Path manifest entry are also considered when they contain META-INF/resources and are exposed to the embedded Tomcat servlet engine.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
backend, web-dev
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.