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
pull/5488/head
Andy Wilkinson 9 years ago
parent 6be92675c2
commit 1d099035b1

@ -73,7 +73,19 @@ public class LaunchedURLClassLoader extends URLClassLoader {
throws ClassNotFoundException {
Handler.setUseFastConnectionExceptions(true);
try {
definePackageIfNecessary(name);
try {
definePackageIfNecessary(name);
}
catch (IllegalArgumentException ex) {
// Tolerate race condition due to being parallel capable
if (getPackage(name) == null) {
// This should never happen as the IllegalArgumentException indicates
// that the package has already been defined and, therefore,
// getPackage(name) should not return null.
throw new AssertionError("Package " + name + " has already been "
+ "defined but it could not be found");
}
}
return super.loadClass(name, resolve);
}
finally {
@ -92,7 +104,20 @@ public class LaunchedURLClassLoader extends URLClassLoader {
if (lastDot >= 0) {
String packageName = className.substring(0, lastDot);
if (getPackage(packageName) == null) {
definePackage(packageName);
try {
definePackage(packageName);
}
catch (IllegalArgumentException ex) {
// Tolerate race condition due to being parallel capable
if (getPackage(packageName) == null) {
// This should never happen as the IllegalArgumentException
// indicates that the package has already been defined and,
// therefore, getPackage(name) should not have returned null.
throw new AssertionError(
"Package " + packageName + " has already been defined "
+ "but it could not be found");
}
}
}
}
}

Loading…
Cancel
Save