`WorkerExecutor::processIsolation` not fully isolated ? System class loader full of gradle jars

I’m trying to understand a behavior on the WorkerExceutor isolation modes.

So I had workerExecutor.noIsolation() to validate some stuff with an old version of Apache HttpClient, and this worked well.
Then I wanted to control the JVM, so I switched to workerExecutor.processIsolation and pass a specific launcher.

However to my surprise, classes from HttpClient 4.5 surfaced.

In a reproducer I noticed that the system class loader was quite different in processIsolation and actually full of Gradle jars, jackson, ant, groovy, etc. While the noIsolation has two jars only !

I am misusing / misconfiguring it ?

=======================================================================================
Running MuzzleAction with mode: Submitted to noIsolation queue

~/.gradle/wrapper/dists/gradle-8.14.2-bin/2pb3mgt1p815evrl3weanttgr/gradle-8.14.2/lib/gradle-daemon-main-8.14.2.jar
~/.gradle/wrapper/dists/gradle-8.14.2-bin/2pb3mgt1p815evrl3weanttgr/gradle-8.14.2/lib/agents/gradle-instrumentation-agent-8.14.2.jar

=======================================================================================
Running MuzzleAction with mode: Submitted to processIsolation queue

~/.gradle/caches/8.14.2/workerMain/gradle-worker.jar
~/.gradle/wrapper/dists/gradle-8.14.2-bin/2pb3mgt1p815evrl3weanttgr/gradle-8.14.2/lib/plugins/gradle-daemon-server-worker-8.14.2.jar
~/.gradle/wrapper/dists/gradle-8.14.2-bin/2pb3mgt1p815evrl3weanttgr/gradle-8.14.2/lib/annotations-24.0.1.jar
~/.gradle/wrapper/dists/gradle-8.14.2-bin/2pb3mgt1p815evrl3weanttgr/gradle-8.14.2/lib/ant-antlr-1.10.15.jar
~/.gradle/wrapper/dists/gradle-8.14.2-bin/2pb3mgt1p815evrl3weanttgr/gradle-8.14.2/lib/ant-launcher-1.10.15.jar

... many other (165 in total)

Reproducer code, needs to run the daemon on a JDK 8.

tasks.register<WorkerIsolationReproducerTask>("reproduceWorkerIsolation") {
  group = "verification"
  description = "Reproduces worker isolation issue with URLClassLoader"
}

open class WorkerIsolationReproducerTask @Inject constructor(
  @Internal val workerExecutor: WorkerExecutor,
  @Internal val javaToolchainService: JavaToolchainService
) : DefaultTask() {

  @TaskAction
  fun reproduce() {
    workerExecutor.noIsolation().submit(MuzzleAction::class.java) {
      mode = "Submitted to noIsolation queue"
    }

    val selectedLauncher = javaToolchainService.launcherFor {
      languageVersion = JavaLanguageVersion.of(8)
    }.get()
    workerExecutor.processIsolation() {
      forkOptions {
        executable(selectedLauncher.executablePath)
      }
    }.submit(MuzzleAction::class.java) {
      mode = "Submitted to processIsolation queue"
    }
  }
}

interface IsolationMode : WorkParameters {
  val mode: Property<String>
}

abstract class MuzzleAction : WorkAction<IsolationMode> {

  override fun execute() {
    println(
      """
        |
        |=======================================================================================
        |Running MuzzleAction with mode: ${parameters.mode.get()}
        |
      """.trimMargin())
    
    val ucpf = URLClassLoader::class.java.getDeclaredField("ucp")
    ucpf.setAccessible(true)

    val ucp: URLClassPath = ucpf.get(ClassLoader.getSystemClassLoader()) as URLClassPath
    ucp.urLs.forEach { println(it.path) }
  }
}

Reproducer worker-isolation.zip (52.6 KB)

Actually I opened an issue, as I think this behavior is counter-intuitive.

I think it is mainly your expectations / investigation that confuses you.

The system class loader of no isolation has only those two jars, as that is what is used to start the Gradle daemon and with no isolation you are running in-process, but your action has some class loaders in the ancestry up to the system class loader where those other jars are then also present and more.

When using a work action with process isolation, you are still in Gradle-land, your work action implements a Gradle interface, your work parameter implements a Gradle interface, the communication between the worker process and the daemon needs Gradle classes. Just when starting the worker process, Gradle flattens the classpath and adds it to the system classpath if possible, probably to save some overhead of class-loader hierarchy or something like that.

If you want to fully control the classpath, you need to inject ExecOperations and use javaExec on it to fully control the classpaths.

I think it is mainly your expectations / investigation that confuses you.

Possibly, but the documentation incite to think about levels of isolation. Intuitively, I would not think that much of the gradle implementation jars will “permeate” through this isolation levels while it doesn’t in noIsolation mode

Indeed, I understand a worker has to leverage Gradle API e.g. WorkAction, of course I also understand some infrastructure for the worker daemon to work and to communicate to the main Daemon. But I would think that Gradle could make processIsolation both isolate process wise, but also to not add Gradle jars on the action system class path. At least not implicitly.

If you want to fully control the classpath, you need to inject ExecOperations and use javaExec on it to fully control the classpaths.

Indeed I was thinking about that, i.e. using the regular noIsolation but inject this in the work action.

while it doesn’t in noIsolation mode

That’s what I said I think you just got wrong probably.
If you would look at all the classloaders up to the system class loader, the no isolation mode should have the same jars available as the process isolation mode plus additional ones not present in process isolation I think.
But you only look at the system classloader.

You are correct the system class loader also has a few other jars in noIsolation, but this remains very limited compared to processIsolation.

Below, I show the details using the URLClassPath.lmap field (Map of each URL opened to its corresponding Loader).

Gradle 8.5, actually 8 jars in noIsolation

Note that the parent CL parent = {Launcher$ExtClassLoader@8624} loads is for JDK extension jars (zipfs, cldr data, etc.).

ClassLoader.getSystemClassLoader() = {Launcher$AppClassLoader@8643} 
 ucp = {URLClassPath@8645} 
  path = {ArrayList@8757}  size = 2
  urls = {Stack@8758}  size = 0
  loaders = {ArrayList@8759}  size = 8
  lmap = {HashMap@8760}  size = 8
   "file:///Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-launcher-8.5.jar" -> {URLClassPath$JarLoader@8763} 
   "file:///Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-core-8.5.jar" -> {URLClassPath$JarLoader@8768} 
   "file:///Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-worker-services-8.5.jar" -> {URLClassPath$JarLoader@8766} 
   "file:///Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/agents/gradle-instrumentation-agent-8.5.jar" -> {URLClassPath$JarLoader@8770} 
   "file:///Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-persistent-cache-8.5.jar" -> {URLClassPath$JarLoader@8769} 
   "file:///Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-base-services-8.5.jar" -> {URLClassPath$JarLoader@8765} 
   "file:///Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-core-api-8.5.jar" -> {URLClassPath$JarLoader@8767} 
   "file:///Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-bootstrap-8.5.jar" -> {URLClassPath$JarLoader@8764} 
  jarHandler = {Handler@8761} 
  closed = false
  acc = {AccessControlContext@8646} 
  lookupCacheURLs = null
  lookupCacheLoader = null
 URLClassLoader.ucp = {URLClassPath@8645} 
 acc = {AccessControlContext@8646} 
 closeables = {WeakHashMap@8647}  size = 0
 initialized = true
 pdcache = {HashMap@8648}  size = 6
 parent = {Launcher$ExtClassLoader@8624} 
...

Gradle 8.5, actually 151 jars in processIsolation

Note that here as well the parent CL parent = {Launcher$ExtClassLoader@2368} loads is for JDK extension jars (zipfs, cldr data, etc.).

ClassLoader.getSystemClassLoader() = {Launcher$AppClassLoader@2367} 
 ucp = {URLClassPath@2377} 
  path = {ArrayList@2418}  size = 151
   0 = {URL@2425} "file:/Users/brice.dutheil/.gradle/caches/8.5/workerMain/gradle-worker.jar"
   1 = {URL@2426} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-core-8.5.jar"
   2 = {URL@2427} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/ant-1.10.13.jar"
   3 = {URL@2428} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/ant-antlr-1.10.12.jar"
   4 = {URL@2429} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/ant-junit-1.10.12.jar"
   5 = {URL@2430} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/ant-launcher-1.10.13.jar"
   6 = {URL@2431} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/antlr4-runtime-4.7.2.jar"
   7 = {URL@2432} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/asm-9.5.jar"
   8 = {URL@2433} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/asm-commons-9.5.jar"
   9 = {URL@2434} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/asm-tree-9.5.jar"
   10 = {URL@2435} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/commons-compress-1.21.jar"
   11 = {URL@2436} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/commons-io-2.11.0.jar"
   12 = {URL@2437} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/commons-lang-2.6.jar"
   13 = {URL@2438} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/failureaccess-1.0.1.jar"
   14 = {URL@2439} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/fastutil-8.5.2-min.jar"
   15 = {URL@2440} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-3.0.17.jar"
   16 = {URL@2441} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-ant-3.0.17.jar"
   17 = {URL@2442} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-astbuilder-3.0.17.jar"
   18 = {URL@2443} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-console-3.0.17.jar"
   19 = {URL@2444} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-datetime-3.0.17.jar"
   20 = {URL@2445} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-dateutil-3.0.17.jar"
   21 = {URL@2446} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-docgenerator-3.0.17.jar"
   22 = {URL@2447} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-groovydoc-3.0.17.jar"
   23 = {URL@2448} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-json-3.0.17.jar"
   24 = {URL@2449} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-nio-3.0.17.jar"
   25 = {URL@2450} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-sql-3.0.17.jar"
   26 = {URL@2451} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-swing-3.0.17.jar"
   27 = {URL@2452} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-templates-3.0.17.jar"
   28 = {URL@2453} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-test-3.0.17.jar"
   29 = {URL@2454} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/groovy-xml-3.0.17.jar"
   30 = {URL@2455} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/guava-32.1.2-jre.jar"
   31 = {URL@2456} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/hamcrest-core-1.3.jar"
   32 = {URL@2457} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/javaparser-core-3.17.0.jar"
   33 = {URL@2458} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/javax.inject-1.jar"
   34 = {URL@2459} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/jsr305-3.0.2.jar"
   35 = {URL@2460} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/junit-4.13.2.jar"
   36 = {URL@2461} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-0.22-milestone-25.jar"
   37 = {URL@2462} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-freebsd-amd64-libcpp-0.22-milestone-25.jar"
   38 = {URL@2463} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-linux-aarch64-0.22-milestone-25.jar"
   39 = {URL@2464} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-linux-aarch64-ncurses5-0.22-milestone-25.jar"
   40 = {URL@2465} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-linux-aarch64-ncurses6-0.22-milestone-25.jar"
   41 = {URL@2466} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-linux-amd64-0.22-milestone-25.jar"
   42 = {URL@2467} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-linux-amd64-ncurses5-0.22-milestone-25.jar"
   43 = {URL@2468} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-linux-amd64-ncurses6-0.22-milestone-25.jar"
   44 = {URL@2469} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-osx-aarch64-0.22-milestone-25.jar"
   45 = {URL@2470} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-osx-amd64-0.22-milestone-25.jar"
   46 = {URL@2471} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-windows-amd64-0.22-milestone-25.jar"
   47 = {URL@2472} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-windows-amd64-min-0.22-milestone-25.jar"
   48 = {URL@2473} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-windows-i386-0.22-milestone-25.jar"
   49 = {URL@2474} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/native-platform-windows-i386-min-0.22-milestone-25.jar"
   50 = {URL@2475} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/qdox-1.12.1.jar"
   51 = {URL@2476} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/slf4j-api-1.7.30.jar"
   52 = {URL@2477} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/tomlj-1.0.0.jar"
   53 = {URL@2478} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/xml-apis-1.4.01.jar"
   54 = {URL@2479} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-base-annotations-8.5.jar"
   55 = {URL@2480} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/annotations-24.0.1.jar"
   56 = {URL@2481} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-base-services-8.5.jar"
   57 = {URL@2482} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-build-operations-8.5.jar"
   58 = {URL@2483} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-hashing-8.5.jar"
   59 = {URL@2484} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-worker-services-8.5.jar"
   60 = {URL@2485} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-base-services-groovy-8.5.jar"
   61 = {URL@2486} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-build-cache-8.5.jar"
   62 = {URL@2487} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/HikariCP-4.0.3.jar"
   63 = {URL@2488} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gson-2.8.9.jar"
   64 = {URL@2489} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/h2-2.2.220.jar"
   65 = {URL@2490} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-build-cache-base-8.5.jar"
   66 = {URL@2491} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-files-8.5.jar"
   67 = {URL@2492} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-functional-8.5.jar"
   68 = {URL@2493} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-build-cache-packaging-8.5.jar"
   69 = {URL@2494} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-snapshots-8.5.jar"
   70 = {URL@2495} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-build-option-8.5.jar"
   71 = {URL@2496} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-cli-8.5.jar"
   72 = {URL@2497} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-messaging-8.5.jar"
   73 = {URL@2498} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kryo-2.24.0.jar"
   74 = {URL@2499} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/minlog-1.2.jar"
   75 = {URL@2500} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/objenesis-2.6.jar"
   76 = {URL@2501} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-core-api-8.5.jar"
   77 = {URL@2502} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-enterprise-logging-8.5.jar"
   78 = {URL@2503} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-logging-api-8.5.jar"
   79 = {URL@2504} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-enterprise-operations-8.5.jar"
   80 = {URL@2505} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-enterprise-workers-8.5.jar"
   81 = {URL@2506} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-file-temp-8.5.jar"
   82 = {URL@2507} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-logging-8.5.jar"
   83 = {URL@2508} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/jansi-1.18.jar"
   84 = {URL@2509} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/jcl-over-slf4j-1.7.30.jar"
   85 = {URL@2510} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/jul-to-slf4j-1.7.30.jar"
   86 = {URL@2511} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/log4j-over-slf4j-1.7.30.jar"
   87 = {URL@2512} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-native-8.5.jar"
   88 = {URL@2513} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/file-events-0.22-milestone-25.jar"
   89 = {URL@2514} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/file-events-linux-aarch64-0.22-milestone-25.jar"
   90 = {URL@2515} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/file-events-linux-amd64-0.22-milestone-25.jar"
   91 = {URL@2516} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/file-events-osx-aarch64-0.22-milestone-25.jar"
   92 = {URL@2517} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/file-events-osx-amd64-0.22-milestone-25.jar"
   93 = {URL@2518} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/file-events-windows-amd64-0.22-milestone-25.jar"
   94 = {URL@2519} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/file-events-windows-amd64-min-0.22-milestone-25.jar"
   95 = {URL@2520} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/file-events-windows-i386-0.22-milestone-25.jar"
   96 = {URL@2521} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/file-events-windows-i386-min-0.22-milestone-25.jar"
   97 = {URL@2522} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-problems-api-8.5.jar"
   98 = {URL@2523} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-persistent-cache-8.5.jar"
   99 = {URL@2524} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-resources-8.5.jar"
   100 = {URL@3165} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-process-services-8.5.jar"
   101 = {URL@3166} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-execution-8.5.jar"
   102 = {URL@3167} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-model-core-8.5.jar"
   103 = {URL@3168} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-stdlib-1.9.20.jar"
   104 = {URL@3169} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-file-collections-8.5.jar"
   105 = {URL@3170} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-file-watching-8.5.jar"
   106 = {URL@3171} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-internal-instrumentation-api-8.5.jar"
   107 = {URL@3172} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-jvm-services-8.5.jar"
   108 = {URL@3173} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-model-groovy-8.5.jar"
   109 = {URL@3174} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-normalization-java-8.5.jar"
   110 = {URL@3175} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-worker-processes-8.5.jar"
   111 = {URL@3176} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-wrapper-shared-8.5.jar"
   112 = {URL@3177} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-kotlin-dsl-8.5.jar"
   113 = {URL@3178} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-kotlin-dsl-shared-runtime-8.5.jar"
   114 = {URL@3179} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-assignment-compiler-plugin-embeddable-1.9.20.jar"
   115 = {URL@3180} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-compiler-embeddable-1.9.20.jar"
   116 = {URL@3181} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-daemon-embeddable-1.9.20.jar"
   117 = {URL@3182} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-reflect-1.9.20.jar"
   118 = {URL@3183} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-sam-with-receiver-compiler-plugin-1.9.20.jar"
   119 = {URL@3184} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-script-runtime-1.9.20.jar"
   120 = {URL@3185} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-scripting-common-1.9.20.jar"
   121 = {URL@3186} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-scripting-compiler-embeddable-1.9.20.jar"
   122 = {URL@3187} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-scripting-compiler-impl-embeddable-1.9.20.jar"
   123 = {URL@3188} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-scripting-jvm-1.9.20.jar"
   124 = {URL@3189} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlin-scripting-jvm-host-1.9.20.jar"
   125 = {URL@3190} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/kotlinx-metadata-jvm-0.5.0.jar"
   126 = {URL@3191} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/trove4j-1.0.20200330.jar"
   127 = {URL@3192} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-kotlin-dsl-tooling-models-8.5.jar"
   128 = {URL@3193} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-tooling-api-8.5.jar"
   129 = {URL@3194} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-runtime-api-info-8.5.jar"
   130 = {URL@3195} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/gradle-dependency-management-8.5.jar"
   131 = {URL@3196} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/httpcore-4.4.14.jar"
   132 = {URL@3197} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/ivy-2.5.2.jar"
   133 = {URL@3198} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/maven-builder-support-3.9.5.jar"
   134 = {URL@3199} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/maven-settings-3.9.5.jar"
   135 = {URL@3200} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/maven-settings-builder-3.9.5.jar"
   136 = {URL@3201} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/plexus-cipher-2.0.jar"
   137 = {URL@3202} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/plexus-interpolation-1.26.jar"
   138 = {URL@3203} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/plexus-sec-dispatcher-2.0.jar"
   139 = {URL@3204} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/plexus-utils-3.5.1.jar"
   140 = {URL@3205} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/gradle-resources-http-8.5.jar"
   141 = {URL@3206} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/commons-codec-1.15.jar"
   142 = {URL@3207} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/httpclient-4.5.13.jar"
   143 = {URL@3208} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/jcifs-1.3.17.jar"
   144 = {URL@3209} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/jsoup-1.15.3.jar"
   145 = {URL@3210} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/gradle-security-8.5.jar"
   146 = {URL@3211} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/bcpg-jdk15on-1.68.jar"
   147 = {URL@3212} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/bcprov-jdk15on-1.68.jar"
   148 = {URL@3213} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/gradle-plugin-use-8.5.jar"
   149 = {URL@3214} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/gradle-workers-8.5.jar"
   150 = {URL@3215} "file:/Users/brice.dutheil/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/plugins/gradle-instrumentation-declarations-8.5.jar"
  urls = {Stack@2419}  size = 0
  loaders = {ArrayList@2420}  size = 151
  lmap = {HashMap@2421}  size = 155
  jarHandler = {Handler@2422} 
  closed = false
  acc = {AccessControlContext@2378} 
  lookupCacheURLs = null
  lookupCacheLoader = null
 URLClassLoader.ucp = {URLClassPath@2377} 
 acc = {AccessControlContext@2378} 
 closeables = {WeakHashMap@2379}  size = 0
 initialized = true
 pdcache = {HashMap@2380}  size = 34
 parent = {Launcher$ExtClassLoader@2368} 
...

I would expect that the system class loader of the WorkAction in processIsolation has the same system class path (i.e same jars), not the whole distribution. But that doesn’t mean the whole worker process do use them in some way.

I still don’t understand why you focus that much on system classloader.
You need to consider the whole ancestry.

In your MCVE here, for example replace

val ucp: URLClassPath = ucpf.get(ClassLoader.getSystemClassLoader()) as URLClassPath
val urls = ucp.urLs
println("""Count: ${urls.size}""")
urls.map { it.path.replace(System.getProperty("user.home"), "~") }.forEach { println(it) }

by

val urls = generateSequence(javaClass.classLoader) { it.parent }
  .filterIsInstance<URLClassLoader>()
  .flatMap {
    val ucp: URLClassPath = ucpf.get(it) as URLClassPath
    ucp.urLs.asSequence()
  }
  .map { it.path }
  .toSortedSet()
println("""Count: ${urls.size}""")
urls.map { it.replace(System.getProperty("user.home"), "~") }.forEach { println(it) }

and you get a more realistic imprint of the situation.

Hi there,

Thanks for bearing with me.

Your suggested code is nice but uses the current class classloader, which I’m not trying to do, the code I’m working specifically uses ClassLoaders.getSystemClassLoader() as parent.

-     val urls = generateSequence(javaClass.classLoader) { it.parent }
+     val urls = generateSequence(ClassLoader.getSystemClassLoader()) { it.parent }

And in this case this goes back to what I showed earlier.

Behind this gymnastic I’m trying to demonstrate / assess is the behavior of classloading for some classes of some common libraries that also appear in the gradle distribution. So when I noticed an unexpected class I was looking at its classloader and noticed the jars on the “class path” of the system classloader that is used as the parent class loader. I then noticed the Gradle jars there.

Concretely the code I’m working with checks the presence of some classes and sometime expects these classes are “not there” in the pass classloader. A simplified version could be :

    val forName =
      try {
        Class.forName("org.apache.http.client.methods.HttpUriRequest", false, ClassLoader.getSystemClassLoader())
      } catch (e: ClassNotFoundException) {
        null
      }
    println("Class.forName: $forName")

If I change the reproducer code to run this code instead of walking the classloaders that’s what I got on Gradle 8.5, the presence of this class is not expected

=======================================================================================
Running MuzzleAction with mode: Submitted to noIsolation queue

Class.forName: null

=======================================================================================
Running MuzzleAction with mode: Submitted to processIsolation queue

Class.forName: interface org.apache.http.client.methods.HttpUriRequest

Now thanks to this discussion, when I used on Gradle 8.14.2 my reproducer that walked the classpath I didn’t notice much difference.

However, on Gradle 8.14.2, when it runs the Class.forName(...) the class is not found which is the expected behavior !

=======================================================================================
Running MuzzleAction with mode: Submitted to noIsolation queue

Class.forName: null

=======================================================================================
Running MuzzleAction with mode: Submitted to processIsolation queue

Class.forName: null

I looked between 8.5 and 8.14.2 to see when this was fixed, it appears that 8.14 is the one that fixed this, 8.13 appears to have the same unexpected (if not buggy) behavior.

That said, the issue might still be present for other classes, as the system classloader still has other “common” jars on the classpath.

I’m unsure what fixed the issue for this class form this release at this time: Gradle 8.14 Release Notes, (because httpclient is still part of the distrib in 8.14.2).

I bisected further, the fix appeared in 8.14-milestone-3 (and the issue was still present in milestone 2).

Looking at the commits I’m not sure exactly which one fixes the issue, at least for this particular httpclient class : Comparing v8.14.0-M2...v8.14.0-M3 · gradle/gradle · GitHub

But this one looks interesting

Or maybe this one

Yes, the first one touches the part where the classpath is constructed and as I said, sometimes uses the full classpath for the worker, sometimes restoring the class-loader hierarchy in the worker process, I forgot when it does which.

No idea why this was changed, as there is no issue referenced in that commit. :man_shrugging:

If it was due to a bug, they should usually have a link to that issue.
Maybe it was just some internal restructuring that happens to better adhere to your expectations. :man_shrugging:

The PRs you linked are indeed internal refactorings, intended to clean up the way we construct worker classpaths. They were not intended to fix any user-facing issue.

The system classloader is just one small piece of the puzzle. According to the ClassLoader#getSystemClassLoader javadoc:

 Returns the system class loader.  This is the default
 delegation parent for new {@code ClassLoader} instances, and is
 typically the class loader used to start the application.

The classpath of this classloader which you are reading in the daemon process is a side-effect of the way the Gradle daemon process starts up. We use a very small classloader and then load the entire daemon classpath from the Gradle distribution on disk.

As Björn mentioned, the important part is the entire classloader hierarchy. In the daemon, we have a number of classloaders built on-top of the system classloaders. In the worker, there are much fewer, as we calculate the worker’s full classpath in the daemon before we start the worker process.

Notice how Class.forName does not use the system classloader, but uses the classloader of the caller class:

    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

This is how classloading traditionally works in Java. When you mention a class name that the JVM hasn’t seen before it will ask the classloader of the current class to load it. This is the core reason why the system classloader is not generally very important. Its the whole hierarchy up to your current class that matters.

In both the worker and daemon case, much of the Gradle runtime is present in the hierarchy.

You noticed the HttpClient classes going away in the worker after my recent changes. This is because the new method of determining the worker classpath is more precise, and we don’t expose any APIs in the worker to make HTTP requests. But, this doesn’t mean they’re not present in the daemon. They are, but “above” the system classloader.

I guess its worth stepping back here.
Why are the contents of the system classloader important to you?

1 Like

Thank you for the answer, indeed, I suggested the following code (as a simplified version of what my action does)

Class.forName("some.pkg.SomeClass", false, ClassLoader.getSystemClassLoader())

So, actually the WorkAction in this project, is building a classloader hierarchy using ClassLoader.getSystemClassLoader() as the parent classloader. In the child classloaders, the task use precise dependencies (like dependency version, etc). The child classloader is then “queried” to see if it has this class or not.

The case were I saw the failure when some types (like org.apache.http.client.methods.HttpUriRequest) were not supposed to be present in the child classloader became “visible”, because they were accessible via the parent system classloader in processIsolation but not in noIsolation.

The ultimate goal is to test the behavior of an instrumenting java agent. And the test code directly use the child classloader, instead of using JDK’s “implicit” behaviors like Class.forName or the TCCL.