Skip to content

Commit 1d09903

Browse files
committed
Protect against a race condition when defining packages
LaunchedURLClassLoader preemptively defines the package for any classes that it attempts to load so that the manifest from a nested jar is correctly associated with the package. This can lead to a race where the package is defined on two threads in parallel, resulting in an IllegalArgumentException being thrown. This problem was manifesting itself as a NoClassDefFoundError. If the initialization of a class failed due to the above-described IllegalArgumentException, subsequent attempts to use that class would then fail with a NoClassDefFoundError. This commit updates LaunchedURLClassLoader to catch the IllegalArgumentException and then double-check that the package has already been defined. This approach, including thrown an AssertionError when the second check fails, is modelled on the approach taken by URLClassLoader. Closes gh-5464
1 parent 6be9267 commit 1d09903

File tree

1 file changed

+27
-2
lines changed

1 file changed

+27
-2
lines changed

spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java

+27-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,19 @@ protected Class<?> loadClass(String name, boolean resolve)
7373
throws ClassNotFoundException {
7474
Handler.setUseFastConnectionExceptions(true);
7575
try {
76-
definePackageIfNecessary(name);
76+
try {
77+
definePackageIfNecessary(name);
78+
}
79+
catch (IllegalArgumentException ex) {
80+
// Tolerate race condition due to being parallel capable
81+
if (getPackage(name) == null) {
82+
// This should never happen as the IllegalArgumentException indicates
83+
// that the package has already been defined and, therefore,
84+
// getPackage(name) should not return null.
85+
throw new AssertionError("Package " + name + " has already been "
86+
+ "defined but it could not be found");
87+
}
88+
}
7789
return super.loadClass(name, resolve);
7890
}
7991
finally {
@@ -92,7 +104,20 @@ private void definePackageIfNecessary(String className) {
92104
if (lastDot >= 0) {
93105
String packageName = className.substring(0, lastDot);
94106
if (getPackage(packageName) == null) {
95-
definePackage(packageName);
107+
try {
108+
definePackage(packageName);
109+
}
110+
catch (IllegalArgumentException ex) {
111+
// Tolerate race condition due to being parallel capable
112+
if (getPackage(packageName) == null) {
113+
// This should never happen as the IllegalArgumentException
114+
// indicates that the package has already been defined and,
115+
// therefore, getPackage(name) should not have returned null.
116+
throw new AssertionError(
117+
"Package " + packageName + " has already been defined "
118+
+ "but it could not be found");
119+
}
120+
}
96121
}
97122
}
98123
}

0 commit comments

Comments
 (0)