sbt.internal.ReverseLookupClassLoader Maven / Gradle / Ivy
Show all versions of main_2.12 Show documentation
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import sbt.util.Logger;
/**
* A ClassLoader for the dependency layer of a run or test task. It is almost a normal
* URLClassLoader except that it has the ability to look one level down the classloading hierarchy
* to load a class via reflection that is not directly available to it. The ClassLoader that is
* below it in the hierarchy must be registered via setDescendant. If it ever loads a class from its
* descendant, then it cannot be used in a subsequent run because it will not be possible to reload
* that class.
*
* The descendant classloader checks it in and out via [[checkout]] and [[checkin]]. When it
* returns the loader via [[checkin]], if the loader is dirty, we close it. Otherwise we cache it if
* there is no existing cache entry.
*
*
Performance degrades if loadClass is constantly looking back up to the provided
* BottomClassLoader so we provide an alternate loadClass definition that takes a reverseLookup
* boolean parameter. Because the [[BottomClassLoader]] knows what loader is calling into, when it
* delegates its search to the ReverseLookupClassLoader, it passes false for the reverseLookup flag.
* By default the flag is true. Most of the time, the default loadClass will only be invoked by java
* reflection calls. Even then, there's some chance that the class being loaded by java reflection
* is _not_ available on the bottom classpath so it is not guaranteed that performing the reverse
* lookup will invalidate this loader.
*/
final class ReverseLookupClassLoader extends ManagedClassLoader {
ReverseLookupClassLoader(
final URL[] urls,
final ClassLoader parent,
final boolean close,
final boolean allowZombies,
final Logger logger) {
super(urls, parent, close, allowZombies, logger);
this.parent = parent;
}
private final AtomicReference directDescendant = new AtomicReference<>();
private final AtomicBoolean dirty = new AtomicBoolean(false);
private final ClassLoadingLock classLoadingLock = new ClassLoadingLock();
private final ClassLoader parent;
boolean isDirty() {
return dirty.get();
}
void setDescendant(final BottomClassLoader bottomClassLoader) {
directDescendant.set(bottomClassLoader);
}
@Override
public URL findResource(String name) {
final URL url = super.findResource(name);
return url != null ? url : directDescendant.get().findResource(name);
}
@Override
public Enumeration findResources(String name) throws IOException {
final Enumeration parentResources = super.findResources(name);
if (directDescendant.get() == null) {
return parentResources;
}
final Enumeration directDescendantResources = directDescendant.get().findResources(name);
return new Enumeration() {
@Override
public boolean hasMoreElements() {
return parentResources.hasMoreElements() || directDescendantResources.hasMoreElements();
}
@Override
public URL nextElement() {
if (parentResources.hasMoreElements()) {
return parentResources.nextElement();
}
return directDescendantResources.nextElement();
}
};
}
void setup(final File tmpDir) {
setTempDir(tmpDir);
}
@Override
protected Class> loadClass(final String name, final boolean resolve)
throws ClassNotFoundException {
return loadClass(name, resolve, true);
}
Class> loadClass(final String name, final boolean resolve, final boolean childLookup)
throws ClassNotFoundException {
Class> result;
try {
result = parent.loadClass(name);
} catch (final ClassNotFoundException e) {
result = findClass(name, childLookup);
}
if (result == null) throw new ClassNotFoundException(name);
if (resolve) resolveClass(result);
return result;
}
private Class> findClass(final String name, final boolean childLookup)
throws ClassNotFoundException {
return classLoadingLock.withLock(
name,
() -> {
try {
final Class> prev = findLoadedClass(name);
if (prev != null) return prev;
return findClass(name);
} catch (final ClassNotFoundException e) {
if (childLookup) {
final BottomClassLoader loader = directDescendant.get();
if (loader == null) throw e;
final Class> clazz = loader.findClass(name);
dirty.set(true);
return clazz;
} else {
throw e;
}
}
});
}
static {
registerAsParallelCapable();
}
}