Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't delegate to parent CL for "leaking" dependencies from build system #4905

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.List;

/**
* A wrapper around URLClassLoader whose only purpose is to expose defineClass
Expand All @@ -10,11 +12,48 @@
*/
public class DefineClassVisibleURLClassLoader extends URLClassLoader {

private static final List<String> KNOWN_LEAKING_PACKAGES_FROM_PARENT_CL = Arrays.asList(
"org.apache.commons.lang3", // commons-lang3 used internally by gradle
"com.google.common" // guava added by maven internals
);

public DefineClassVisibleURLClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}

public Class<?> visibleDefineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
return super.defineClass(name, b, off, len);
}

public Class<?> loadClass(String name) throws ClassNotFoundException {
boolean lookup = false;
for (String prefix : KNOWN_LEAKING_PACKAGES_FROM_PARENT_CL) {
if (name.startsWith(prefix)) {
lookup = true;
break;
}
}

// The parent classloader can introduce plenty of build system dependencies (like commons-lang3 or guava)
// Which could be incompatible with what the user dependencies specify
// The temporary solution is to not delegate to parent class loader for dependencies that are known
// to leak in from the build system.
// The solution is pretty bad and should be views as temporary until a proper solution
// (like the one proposed here: https://github.com/quarkusio/quarkus/issues/770)
if (lookup) {
Class<?> loadedClass = findLoadedClass(name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might be problematic. What is Gradle has already loaded some classes from guava? findLoadedClass will return the one loaded by the base class loader, but any classes that have not been loaded yet will be loaded by this class loader, breaking any package protected access and loading different versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am assuming that would only happen if some plugin used in the build depended on Guava and was unshaded. It could probably happen, but I am assuming it won't be very likely.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would it be in the build classpath if it is not being used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guava sneaks in (or at least used to sneak in) because it was a dependency of the maven resolver, or some other library we use in boostrap.

if (loadedClass == null) {
try {
loadedClass = findClass(name);
} catch (ClassNotFoundException ignored) {
}

if (loadedClass == null) {
loadedClass = super.loadClass(name);
}
}
return loadedClass;
}
return super.loadClass(name);
}
}