From 1d099035b1f732efdc36221d071402fa5f05fe43 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 24 Mar 2016 15:32:39 +0000 Subject: [PATCH] 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 --- .../boot/loader/LaunchedURLClassLoader.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java index 1608792e7f..81cc71e671 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java @@ -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"); + } + } } } }