-
Notifications
You must be signed in to change notification settings - Fork 7
fix(resolver):Fix classloader issue for Spark/isolated environments #9
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -90,6 +90,16 @@ public class ArtifactResolver | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .add("http://repo.maven.apache.org/maven2/") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static final String MAVEN_REPOSITORY_SYSTEM_CLASS = "org.apache.maven.repository.RepositorySystem"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static final String PLEXUS_CONTAINER_CLASS = "org.codehaus.plexus.DefaultPlexusContainer"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static final String AETHER_REPOSITORY_SYSTEM_CLASS = "org.eclipse.aether.RepositorySystem"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Cached effective ClassLoader to avoid repeated Class.forName checks on every container() call. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Initialized lazily on first access and reused thereafter. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static volatile ClassLoader cachedEffectiveClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final RepositorySystem repositorySystem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final DefaultRepositorySystemSession repositorySystemSession; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final List<RemoteRepository> repositories; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -327,7 +337,11 @@ private static PlexusContainer container() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: move off Plexus DI, use Sisu instead | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ClassWorld classWorld = new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fix for Spark and other isolated classloader environments: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Select an effective ClassLoader by checking multiple candidates to ensure Maven Resolver/Plexus components are discoverable at runtime. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ClassLoader classLoader = getEffectiveClassLoader(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ClassWorld classWorld = new ClassWorld("plexus.core", classLoader); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContainerConfiguration cc = new DefaultContainerConfiguration() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .setClassWorld(classWorld) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -350,4 +364,78 @@ private static PlexusContainer container() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new RuntimeException("Error loading Maven system", e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Determine a ClassLoader that can access Maven Resolver and Plexus classes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Some isolated runtimes (e.g. Spark executors) use a TCCL that cannot see | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * resolver dependencies, so we fall back to the resolver's defining loader. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+368
to
+372
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I don't think we need a long javadoc. I would suggest trimming it down.
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static ClassLoader getEffectiveClassLoader() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this method should be
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // First check without synchronization (fast path for already cached value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ClassLoader cached = cachedEffectiveClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (cached != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return cached; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Synchronize for initialization to prevent race conditions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| synchronized (ArtifactResolver.class) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change cannot be applied because it causes a compilation error. The |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Double-check after acquiring lock | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cached = cachedEffectiveClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (cached != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return cached; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (contextClassLoader != null && canLoadMavenComponents(contextClassLoader)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cachedEffectiveClassLoader = contextClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return cachedEffectiveClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ClassLoader thisClassLoader = ArtifactResolver.class.getClassLoader(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (thisClassLoader != null && canLoadMavenComponents(thisClassLoader)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cachedEffectiveClassLoader = thisClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return cachedEffectiveClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (systemClassLoader != null && canLoadMavenComponents(systemClassLoader)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cachedEffectiveClassLoader = systemClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return cachedEffectiveClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ClassLoader fallback = systemClassLoader != null ? systemClassLoader : thisClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (fallback == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new IllegalStateException("Unable to determine a valid ClassLoader for Maven component initialization. " + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Both system ClassLoader and class ClassLoader are null, and no other ClassLoader can load required Maven components."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cachedEffectiveClassLoader = fallback; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return cachedEffectiveClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Verify whether the provided ClassLoader has visibility into essential Maven | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * and Plexus components required for resolver initialization. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param classLoader the ClassLoader to validate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return true if required Maven and Plexus classes are visible to the ClassLoader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * false otherwise | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static boolean canLoadMavenComponents(ClassLoader classLoader) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Verify visibility of a core Maven repository component | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Class.forName(MAVEN_REPOSITORY_SYSTEM_CLASS, false, classLoader); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Verify visibility of the Plexus container used for component discovery | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Class.forName(PLEXUS_CONTAINER_CLASS, false, classLoader); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Verify visibility of Maven Resolver API (critical for dependency resolution) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Class.forName(AETHER_REPOSITORY_SYSTEM_CLASS, false, classLoader); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catch (ClassNotFoundException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One question: for my understanding, what was the reason behind choosing these three specific classes? Was it based on the original error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, does checking the visibility of these classes guarantee that the class loader will have visibility of all required components?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@imjalpreet
Yes, these three classes were chosen based on the original
ClassNotFoundExceptionerrors encountered in isolated classloader environments (specifically Spark executors). If all three are visible, it strongly indicates the ClassLoader has access to the complete Maven ecosystem, including all transitive dependencies.No, it doesn't provide an absolute guarantee, but it provides a very strong indication.