Class ScalaProcessingClassLoader

java.lang.Object
java.lang.ClassLoader
java.security.SecureClassLoader
java.net.URLClassLoader
io.micronaut.scala.processing.visitor.ScalaProcessingClassLoader
All Implemented Interfaces:
Closeable, AutoCloseable

@Internal public final class ScalaProcessingClassLoader extends URLClassLoader
The classloader a compilation's visitors run in: the compiler's output directory and the compilation classpath, searched before the plugin for everything a visitor does not have to share with it.

Parent-first delegation is the wrong default here, because the parent chain does not end at the plugin: the plugin's loader falls back to the loader that loaded the compiler, and under Gradle that loader is built from the whole scalaClasspath -- not just the compiler but scaladoc and everything scaladoc depends on, among them Jackson, jackson-dataformat-yaml, snakeyaml-engine, flexmark and jsoup. Those are micronaut-openapi's dependencies too, so a parent-first loader handed the OpenAPI visitor the compiler's copies -- Jackson 3.1.2 where the project declared 3.1.5, jsoup 1.22.2 where it declared 1.23.2 -- and not one class from the compilation classpath. That loader is also one Gradle caches and shares between every compilation in a compiler daemon, and closes when it evicts it, so whether a class still resolves through it depends on what earlier compilations loaded. The moment one class of a package came from there and the next one did not, the package was split across two loaders, which the JVM reports as IllegalAccessError: class LoadSettings tried to access method LoadSettingsBuilder.<init>(): the package-private access the two classes are entitled to only while they share a loader.

So the compilation classpath is searched first, and the parent only for what a visitor must share with the plugin: what the JDK supplies, the compiler's own classes (an element's native type is a dotty symbol), the plugin's own package, and whatever the plugin jar bundles -- Micronaut, whose Element API both sides implement, and the annotations it ships. Bundled is decided by looking in the jar, not by package name: io.micronaut.validation.visitor is a Micronaut package too, but it is a visitor on the compilation classpath, and the constraint validators it finds by the Class of an annotation have to see the same Digits the compilation classpath gave it, or it reports "Cannot find a constraint validator" for a constraint it has. That is the model javac gives a visitor, whose processor path carries the visitor and its dependencies together, and it makes the versions a project declares the versions its visitors run with. Resources follow the same rule as classes, so a class and its class file are always read from the same place.