Spring-boot: Guava ClassPath doesn't work after upgrading 1.4.0.M1

Created on 4 Mar 2016  路  3Comments  路  Source: spring-projects/spring-boot

After upgrading 1.4.0.M1, Guava ClassPath doesn't work with repackaged jar file:

./gradlew clean bootRepackage; java -jar build/libs/spring-boot-throwaway-branches-1.0.jar
classes: []

With 1.3.3.RELEASE, it works:

classes: [com.izeye.throwaway.Application, com.izeye.throwaway.Person]

The following branch is a sample project reproducing the problem:

https://github.com/izeye/spring-boot-throwaway-branches/commits/guava

I guess it's affected by the same cause with #5323

Do I report to a wrong place again?

declined

Most helpful comment

Prior to 1.4.0, Boot packages an application's classes in the root of the jar file. It then used an unconventional delegation model in LaunchedURLClassLoader to prevent those classes from being loaded by the app class loader. This broke a few things, including a number of Java agents. 1.4 fixes the problem by moving application classes into BOOT-INF/classes. This hides the classes from the app class loader, allowing LaunchedURLClassLoader to use a conventional delegation model.

Guava's ClassPath only supports URLClassLoader and file protocol URLs. It was previously able to find application classes in an executable jar as the jar itself was on the classpath with a file URL and the classes were in the jar's root. It's never been able to find classes in a nested jar, or application classes or nested jars in an executable war. The move to using BOOT-INF/classes has added an executable jar's application classes to the list of things affected by ClassPath's limitations.

I'd recommend using something that's less limited if you need to scan the classpath. Spring Framework's PathMatchingResourcePatternResolver supports much more than just file URLs and works nicely in your example:

Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:com/izeye/throwaway/**/*");
System.out.println("classes: " + Arrays.toString(resources));

Produces:

classes: [URL [jar:file:/Users/awilkinson/dev/temp/spring-boot-throwaway-branches/build/libs/spring-boot-throwaway-branches-1.0.jar!/BOOT-INF/classes!/com/izeye/throwaway/Application.class], URL [jar:file:/Users/awilkinson/dev/temp/spring-boot-throwaway-branches/build/libs/spring-boot-throwaway-branches-1.0.jar!/BOOT-INF/classes!/com/izeye/throwaway/Person.class]]

It'll also find classes in nested jar files.

All 3 comments

This could be due to the BOOT-INF/lib and BOOT-INF/classes changes. I'll take a look.

Prior to 1.4.0, Boot packages an application's classes in the root of the jar file. It then used an unconventional delegation model in LaunchedURLClassLoader to prevent those classes from being loaded by the app class loader. This broke a few things, including a number of Java agents. 1.4 fixes the problem by moving application classes into BOOT-INF/classes. This hides the classes from the app class loader, allowing LaunchedURLClassLoader to use a conventional delegation model.

Guava's ClassPath only supports URLClassLoader and file protocol URLs. It was previously able to find application classes in an executable jar as the jar itself was on the classpath with a file URL and the classes were in the jar's root. It's never been able to find classes in a nested jar, or application classes or nested jars in an executable war. The move to using BOOT-INF/classes has added an executable jar's application classes to the list of things affected by ClassPath's limitations.

I'd recommend using something that's less limited if you need to scan the classpath. Spring Framework's PathMatchingResourcePatternResolver supports much more than just file URLs and works nicely in your example:

Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:com/izeye/throwaway/**/*");
System.out.println("classes: " + Arrays.toString(resources));

Produces:

classes: [URL [jar:file:/Users/awilkinson/dev/temp/spring-boot-throwaway-branches/build/libs/spring-boot-throwaway-branches-1.0.jar!/BOOT-INF/classes!/com/izeye/throwaway/Application.class], URL [jar:file:/Users/awilkinson/dev/temp/spring-boot-throwaway-branches/build/libs/spring-boot-throwaway-branches-1.0.jar!/BOOT-INF/classes!/com/izeye/throwaway/Person.class]]

It'll also find classes in nested jar files.

@wilkinsona Thanks for the detail and the tip!

Was this page helpful?
0 / 5 - 0 ratings