Make ZombieClassLoader thread safe

The previous implementation of ZombieClassLoader was not thread safe.
This caused problems because it is possible for the ManagedClassLoader
in test to leak into the coursier thread pool if the test uses bouncy
castle apis. Unfortunately, these apis seem to in some cases assign
static variables using the Thread context class loader. Because the
bouncycastle apis are implemented by the jdk, they are found in the
system classloader and thus the static references leak out of the test
context.

I had a local repro of https://github.com/sbt/sbt/issues/5249 that is
fixed by this change.
This commit is contained in:
Ethan Atkins 2019-12-03 18:43:18 -08:00
parent e5e462f2c2
commit 1438b79378
1 changed files with 11 additions and 3 deletions

View File

@ -39,13 +39,16 @@ abstract class ManagedClassLoader extends URLClassLoader implements NativeLoader
private final URL[] urls; private final URL[] urls;
ZombieClassLoader(URL[] urls) { ZombieClassLoader(URL[] urls) {
super(urls, ManagedClassLoader.this); super(urls, ManagedClassLoader.this.getParent());
this.urls = urls; this.urls = urls;
} }
Class<?> lookupClass(final String name) throws ClassNotFoundException { Class<?> lookupClass(final String name) throws ClassNotFoundException {
try { try {
return findClass(name); synchronized (getClassLoadingLock(name)) {
final Class<?> previous = findLoadedClass(name);
return previous != null ? previous : findClass(name);
}
} catch (final ClassNotFoundException e) { } catch (final ClassNotFoundException e) {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
for (final URL u : urls) { for (final URL u : urls) {
@ -89,7 +92,12 @@ abstract class ManagedClassLoader extends URLClassLoader implements NativeLoader
@Override @Override
protected Class<?> findClass(String name) throws ClassNotFoundException { protected Class<?> findClass(String name) throws ClassNotFoundException {
return closed.get() ? getZombieLoader(name).lookupClass(name) : super.findClass(name); try {
return super.findClass(name);
} catch (final NoClassDefFoundError | ClassNotFoundException e) {
if (closed.get()) return getZombieLoader(name).lookupClass(name);
else throw e;
}
} }
@Override @Override