skip to content

Inside a repository(name) { } entry, what is resolverClass, and what does the bound type have to implement for provisioning to work?

level: seniorimportance: should knowfreq 18%

answer

  1. resolverClass binds name → JavaToolchainResolver
  2. resolve(JavaToolchainRequest): Optional<JavaToolchainDownload>
  3. request = spec + build platform
  4. empty Optional = try next repo
  5. class supplied by a settings plugin

basics

~10 s

resolverClass binds a repository name to a JavaToolchainResolver implementation. That class takes a toolchain request (version, vendor, platform) and returns an optional download URI Gradle uses to fetch the JDK.

solid answer

~40 s

Each `repository("name") { resolverClass = SomeResolver::class.java }` maps a name to a `JavaToolchainResolver` implementation — the SPI that turns a toolchain *spec* into a downloadable JDK. The resolver implements `resolve(JavaToolchainRequest): Optional<JavaToolchainDownload>`. The `JavaToolchainRequest` carries the requested `JavaToolchainSpec` (language version, vendor, JVM implementation) plus the `BuildPlatform` (OS + architecture) so the resolver can pick the right native build. Returning an empty `Optional` means "I can't satisfy this" and Gradle moves to the next repository; returning a `JavaToolchainDownload` (built from a URI) tells Gradle where to download. The resolver class itself is provided by a settings plugin applied in the same settings.gradle.kts; resolverClass just references the already-available type.

code

kotlin · 10 lines
kotlin
// A minimal custom resolver (in buildSrc / a settings plugin)
abstract class InternalMirrorResolver : JavaToolchainResolver {
    override fun resolve(request: JavaToolchainRequest): Optional<JavaToolchainDownload> {
        val spec = request.javaToolchainSpec
        val platform = request.buildPlatform // OS + arch
        val version = spec.languageVersion.get().asInt()
        val uri = URI("https://jdks.acme.internal/temurin/$version/${platform.operatingSystem}-${platform.architecture}.tar.gz")
        return Optional.of(JavaToolchainDownload.fromUri(uri))
    }
}

go deeper

for a junior

Know resolverClass points at the plugin class that knows how to download JDKs; usually it's Foojay's.

for a middle

Explain that it binds a name to a JavaToolchainResolver and that the resolver maps a spec to a download URI.

for a senior

Detail the resolve() SPI, the request's spec + platform, and empty-Optional fallthrough semantics.

for a principal

Design custom resolvers against an internal mirror, handle all spec dimensions and platforms, and govern them as a shared settings plugin.

## The binding `resolverClass` is a `Property<Class<out JavaToolchainResolver>>` on the repository handler. It's how a human-readable repository name is wired to the code that actually computes download URLs: ```kotlin repository("foojay") { resolverClass = org.gradle.toolchains.foojay.FoojayToolchainResolver::class.java } ``` The referenced class must already be on the settings classpath — that's why you apply a settings plugin (`plugins { id("...foojay-resolver") }`) that bundles the resolver. ## The JavaToolchainResolver SPI A resolver is a service: ```java public interface JavaToolchainResolver { Optional<JavaToolchainDownload> resolve(JavaToolchainRequest request); } ``` - **`JavaToolchainRequest`** = `getJavaToolchainSpec()` (the requested language version, vendor, implementation — VENDOR_SPECIFIC vs J9) + `getBuildPlatform()` (OS and CPU architecture of the machine that needs the JDK). - The resolver inspects the spec, decides whether it can serve it, and returns either: - `Optional.empty()` → not my job, try the next repository, or - `Optional.of(JavaToolchainDownload.fromUri(uri))` → fetch the JDK archive from this URI. Gradle then downloads the archive, verifies/extracts it, and caches it in the shared toolchains store. ## Why an SPI and not a URL list The spec is multidimensional (version × vendor × implementation × OS × arch). A static URL table can't compute the right artifact for every combination, but a resolver can — Foojay's, for instance, queries the Disco API to map a spec to a concrete distribution download. Custom resolvers map specs to an internal mirror's layout. ## Registration as a service Resolvers can also be registered programmatically by a plugin via `JavaToolchainResolverRegistry.register(MyResolver::class.java)`, then named in `toolchainManagement`. The `resolverClass` in the DSL is the declarative half of that wiring. ## Platform awareness matters Because `BuildPlatform` is part of the request, a resolver returns OS/arch-correct binaries — e.g. an aarch64 macOS JDK on Apple Silicon, x64 Linux on a CI runner. A resolver that ignores the platform would hand back wrong-architecture archives.

  • What does returning Optional.empty() from a resolver mean?
    It signals the resolver can't satisfy this spec, so Gradle moves on to the next registered repository in declared order. If none can, provisioning fails.
  • Why does the request include a BuildPlatform?
    JDK binaries are OS/architecture-specific. The platform lets the resolver return the correct archive (e.g. aarch64 macOS vs x64 Linux) for the machine that needs the toolchain.

saying these in an interview costs you the question

  • Saying resolverClass takes a URL string — it takes a JavaToolchainResolver class.
  • Forgetting the resolver must consider the build platform and returning a single OS/arch binary for all machines.

context