Skip to content

Commit

Permalink
Protect against a race condition when defining packages
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wilkinsona committed Mar 24, 2016
1 parent 6be9267 commit 1d09903
Showing 1 changed file with 27 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,19 @@ protected Class<?> loadClass(String name, boolean resolve)
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 {
Expand All @@ -92,7 +104,20 @@ private void definePackageIfNecessary(String className) {
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");
}
}
}
}
}
Expand Down

0 comments on commit 1d09903

Please sign in to comment.