Browse Source

Resolve runtime java via Gradle tool chain provider (#95319)

Using gradle toolchain support in gradle requires refactoring how the composite build is composed. 
We added three toolchain resolver
1. Resolver for resolving defined bundled version from oracle as openjdk
2. Resolve all available jdks from Adoption
3. Resolve archived Oracle jdk distributions. 

We should be able to remove the JdkDownloadPlugin altogether without having that in place, but we'll do that in a separate effort.

Fixes #95094
Rene Groeschke 2 years ago
parent
commit
4e2f852c08
22 changed files with 807 additions and 39 deletions
  1. 2 4
      build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/GitInfoPlugin.java
  2. 23 13
      build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/util/Util.java
  3. 4 0
      build-tools-internal/build.gradle
  4. 5 0
      build-tools-internal/settings.gradle
  5. 7 7
      build-tools-internal/src/main/groovy/elasticsearch.ide.gradle
  6. 6 2
      build-tools-internal/src/main/groovy/elasticsearch.runtime-jdk-provision.gradle
  7. 7 1
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/JdkDownloadPlugin.java
  8. 37 5
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java
  9. 42 0
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AbstractCustomJavaToolchainResolver.java
  10. 125 0
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolver.java
  11. 82 0
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolver.java
  12. 28 0
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/JavaToolChainResolverPlugin.java
  13. 99 0
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolver.java
  14. 107 0
      build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AbstractToolchainResolverSpec.groovy
  15. 83 0
      build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolverSpec.groovy
  16. 62 0
      build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolverSpec.groovy
  17. 51 0
      build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolverSpec.groovy
  18. 4 0
      build-tools/settings.gradle
  19. 7 0
      build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java
  20. 0 2
      build.gradle
  21. 25 4
      settings.gradle
  22. 1 1
      test/test-clusters/build.gradle

+ 2 - 4
build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/GitInfoPlugin.java

@@ -9,6 +9,7 @@
 package org.elasticsearch.gradle.internal.conventions;
 
 import org.elasticsearch.gradle.internal.conventions.info.GitInfo;
+import org.elasticsearch.gradle.internal.conventions.util.Util;
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.model.ObjectFactory;
@@ -35,10 +36,7 @@ class GitInfoPlugin implements Plugin<Project> {
 
     @Override
     public void apply(Project project) {
-        File rootDir = (project.getGradle().getParent() == null) ?
-                project.getRootDir() :
-                project.getGradle().getParent().getRootProject().getRootDir();
-
+        File rootDir = Util.locateElasticsearchWorkspace(project.getGradle());
         gitInfo = objectFactory.property(GitInfo.class).value(factory.provider(() ->
             GitInfo.gitInfo(rootDir)
         ));

+ 23 - 13
build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/util/Util.java

@@ -21,6 +21,7 @@ import org.gradle.api.tasks.util.PatternFilterable;
 
 import javax.annotation.Nullable;
 import java.io.File;
+import java.util.Collection;
 import java.util.Optional;
 import java.util.function.Supplier;
 
@@ -105,7 +106,6 @@ public class Util {
         return project.getExtensions().getByType(JavaPluginExtension.class) == null;
     }
 
-
     public static Object toStringable(Supplier<String> getter) {
         return new Object() {
             @Override
@@ -119,21 +119,31 @@ public class Util {
         return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets();
     }
 
-    public static File locateElasticsearchWorkspace(Gradle project) {
-        if (project.getParent() == null) {
-            // See if any of these included builds is the Elasticsearch project
-            for (IncludedBuild includedBuild : project.getIncludedBuilds()) {
-                File versionProperties = new File(includedBuild.getProjectDir(), "build-tools-internal/version.properties");
-                if (versionProperties.exists()) {
-                    return includedBuild.getProjectDir();
+        public static File locateElasticsearchWorkspace(Gradle gradle) {
+            if(gradle.getRootProject().getName().startsWith("build-tools")) {
+                File buildToolsParent = gradle.getRootProject().getRootDir().getParentFile();
+                if(versionFileExists(buildToolsParent)) {
+                    return buildToolsParent;
                 }
+                return buildToolsParent;
             }
+            if (gradle.getParent() == null) {
+                // See if any of these included builds is the Elasticsearch gradle
+                for (IncludedBuild includedBuild : gradle.getIncludedBuilds()) {
+                    if (versionFileExists(includedBuild.getProjectDir())) {
+                        return includedBuild.getProjectDir();
+                    }
+                }
 
-            // Otherwise assume this project is the root elasticsearch workspace
-            return project.getRootProject().getRootDir();
-        } else {
-            // We're an included build, so keep looking
-            return locateElasticsearchWorkspace(project.getParent());
+                // Otherwise assume this gradle is the root elasticsearch workspace
+                return gradle.getRootProject().getRootDir();
+            } else {
+                // We're an included build, so keep looking
+                return locateElasticsearchWorkspace(gradle.getParent());
+            }
         }
+
+    private static boolean versionFileExists(File rootDir) {
+        return new File(rootDir, "build-tools-internal/version.properties").exists();
     }
 }

+ 4 - 0
build-tools-internal/build.gradle

@@ -107,6 +107,10 @@ gradlePlugin {
       id = 'elasticsearch.internal-test-rerun'
       implementationClass = 'org.elasticsearch.gradle.internal.test.rerun.TestRerunPlugin'
     }
+    javaToolChainPlugin {
+      id = 'elasticsearch.java-toolchain'
+      implementationClass = 'org.elasticsearch.gradle.internal.toolchain.JavaToolChainResolverPlugin'
+    }
     javaDoc {
       id = 'elasticsearch.java-doc'
       implementationClass = 'org.elasticsearch.gradle.internal.ElasticsearchJavadocPlugin'

+ 5 - 0
build-tools-internal/settings.gradle

@@ -1,3 +1,8 @@
+pluginManagement {
+    includeBuild "../build-conventions"
+    includeBuild "../build-tools"
+}
+
 dependencyResolutionManagement {
     versionCatalogs {
         buildLibs {

+ 7 - 7
build-tools-internal/src/main/groovy/elasticsearch.ide.gradle

@@ -245,20 +245,20 @@ Node parseXml(Object path) {
   return xml
 }
 
-Pair<File, IncludedBuild> locateElasticsearchWorkspace(Gradle project) {
-  if (project.parent == null) {
-    // See if any of these included builds is the Elasticsearch project
-    for (IncludedBuild includedBuild : project.includedBuilds) {
+Pair<File, IncludedBuild> locateElasticsearchWorkspace(Gradle gradle) {
+  if (gradle.parent == null) {
+    // See if any of these included builds is the Elasticsearch gradle
+    for (IncludedBuild includedBuild : gradle.includedBuilds) {
       File versionProperties = new File(includedBuild.projectDir, 'build-tools-internal/version.properties')
       if (versionProperties.exists()) {
         return Pair.of(includedBuild.projectDir, includedBuild)
       }
     }
 
-    // Otherwise assume this project is the root elasticsearch workspace
-    return Pair.of(project.getRootProject().getRootDir(), null)
+    // Otherwise assume this gradle is the root elasticsearch workspace
+    return Pair.of(gradle.getRootProject().getRootDir(), null)
   } else {
     // We're an included build, so keep looking
-    return locateElasticsearchWorkspace(project.parent)
+    return locateElasticsearchWorkspace(gradle.parent)
   }
 }

+ 6 - 2
build-tools-internal/src/main/groovy/elasticsearch.runtime-jdk-provision.gradle

@@ -35,8 +35,12 @@ configure(allprojects) {
             test.executable = "${BuildParams.runtimeJavaHome}/bin/java" +
                     (OS.current() == OS.WINDOWS ? '.exe' : '')
         } else {
-            test.dependsOn(project.jdks.provisioned_runtime)
-            test.executable = rootProject.jdks.provisioned_runtime.getBinJavaPath()
+            test.javaLauncher = javaToolchains.launcherFor {
+                languageVersion = JavaLanguageVersion.of(VersionProperties.bundledJdkMajorVersion)
+                vendor = VersionProperties.bundledJdkVendor == "openjdk" ?
+                        JvmVendorSpec.ORACLE :
+                        JvmVendorSpec.matching(VersionProperties.bundledJdkVendor)
+            }
         }
     }
     project.plugins.withId("elasticsearch.testclusters") { testClustersPlugin ->

+ 7 - 1
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/JdkDownloadPlugin.java

@@ -22,6 +22,13 @@ import org.gradle.api.attributes.Attribute;
 
 import java.util.Arrays;
 
+/**
+ * @deprecated We wanna get rid from this and custom jdk downloads via this plugin and
+ * make leverage the gradle toolchain resolver capabilities.
+ *
+ * @See @org.elasticsearch.gradle.internal.toolchain.JavaToolChainResolverPlugin
+ * */
+@Deprecated
 public class JdkDownloadPlugin implements Plugin<Project> {
 
     public static final String VENDOR_ADOPTIUM = "adoptium";
@@ -161,7 +168,6 @@ public class JdkDownloadPlugin implements Plugin<Project> {
     private static String dependencyNotation(Jdk jdk) {
         String platformDep = isJdkOnMacOsPlatform(jdk) ? (jdk.getVendor().equals(VENDOR_ADOPTIUM) ? "mac" : "macos") : jdk.getPlatform();
         String extension = jdk.getPlatform().equals("windows") ? "zip" : "tar.gz";
-
         return groupName(jdk) + ":" + platformDep + ":" + jdk.getBaseVersion() + ":" + jdk.getArchitecture() + "@" + extension;
     }
 

+ 37 - 5
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java

@@ -20,6 +20,9 @@ import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.api.model.ObjectFactory;
+import org.gradle.api.plugins.JvmToolchainsPlugin;
+import org.gradle.api.provider.Property;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.provider.ProviderFactory;
 import org.gradle.internal.jvm.Jvm;
@@ -27,11 +30,14 @@ import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
 import org.gradle.internal.jvm.inspection.JvmMetadataDetector;
 import org.gradle.internal.jvm.inspection.JvmVendor;
 import org.gradle.jvm.toolchain.JavaLanguageVersion;
+import org.gradle.jvm.toolchain.JavaLauncher;
+import org.gradle.jvm.toolchain.JavaToolchainService;
 import org.gradle.jvm.toolchain.JavaToolchainSpec;
 import org.gradle.jvm.toolchain.JvmVendorSpec;
 import org.gradle.jvm.toolchain.internal.InstallationLocation;
 import org.gradle.jvm.toolchain.internal.JavaInstallationRegistry;
 import org.gradle.util.GradleVersion;
+import org.jetbrains.annotations.NotNull;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -56,19 +62,24 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
     private static final Logger LOGGER = Logging.getLogger(GlobalBuildInfoPlugin.class);
     private static final String DEFAULT_VERSION_JAVA_FILE_PATH = "server/src/main/java/org/elasticsearch/Version.java";
 
+    private ObjectFactory objectFactory;
     private final JavaInstallationRegistry javaInstallationRegistry;
     private final JvmMetadataDetector metadataDetector;
     private final ProviderFactory providers;
+    private JavaToolchainService toolChainService;
 
     @Inject
     public GlobalBuildInfoPlugin(
+        ObjectFactory objectFactory,
         JavaInstallationRegistry javaInstallationRegistry,
         JvmMetadataDetector metadataDetector,
         ProviderFactory providers
     ) {
+        this.objectFactory = objectFactory;
         this.javaInstallationRegistry = javaInstallationRegistry;
         this.metadataDetector = new ErrorTraceMetadataDetector(metadataDetector);
         this.providers = providers;
+
     }
 
     @Override
@@ -76,6 +87,8 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
         if (project != project.getRootProject()) {
             throw new IllegalStateException(this.getClass().getName() + " can only be applied to the root project.");
         }
+        project.getPlugins().apply(JvmToolchainsPlugin.class);
+        toolChainService = project.getExtensions().getByType(JavaToolchainService.class);
         GradleVersion minimumGradleVersion = GradleVersion.version(getResourceContents("/minimumGradleVersion"));
         if (GradleVersion.current().compareTo(minimumGradleVersion) < 0) {
             throw new GradleException("Gradle " + minimumGradleVersion.getVersion() + "+ is required");
@@ -114,6 +127,8 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
             params.setTestSeed(getTestSeed());
             params.setIsCi(System.getenv("JENKINS_URL") != null || System.getenv("BUILDKITE_BUILD_URL") != null);
             params.setDefaultParallel(ParallelDetector.findDefaultParallel(project));
+            // TODO: Test if cc issues are coming from here
+            params.setDefaultParallel(8);
             params.setInFipsJvm(Util.getBooleanProperty("tests.fips.enabled", false));
             params.setIsSnapshotBuild(Util.getBooleanProperty("build.snapshot", true));
             AtomicReference<BwcVersions> cache = new AtomicReference<>();
@@ -219,7 +234,7 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
 
     private boolean isSameFile(File javaHome, InstallationLocation installationLocation) {
         try {
-            return Files.isSameFile(installationLocation.getLocation().toPath(), javaHome.toPath());
+            return Files.isSameFile(javaHome.toPath(), installationLocation.getLocation().toPath());
         } catch (IOException ioException) {
             throw new UncheckedIOException(ioException);
         }
@@ -294,9 +309,14 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
     }
 
     private String findJavaHome(String version) {
-        Provider<String> javaHomeNames = providers.gradleProperty("org.gradle.java.installations.fromEnv");
         String javaHomeEnvVar = getJavaHomeEnvVarName(version);
+        String env = System.getenv(javaHomeEnvVar);
+        return env != null ? resolveJavaHomeFromEnvVariable(javaHomeEnvVar) : resolveJavaHomeFromToolChainService(version);
+    }
 
+    @NotNull
+    private String resolveJavaHomeFromEnvVariable(String javaHomeEnvVar) {
+        Provider<String> javaHomeNames = providers.gradleProperty("org.gradle.java.installations.fromEnv");
         // Provide a useful error if we're looking for a Java home version that we haven't told Gradle about yet
         Arrays.stream(javaHomeNames.get().split(","))
             .filter(s -> s.equals(javaHomeEnvVar))
@@ -309,7 +329,6 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
                         + "updated in gradle.properties file."
                 )
             );
-
         String versionedJavaHome = System.getenv(javaHomeEnvVar);
         if (versionedJavaHome == null) {
             final String exceptionMessage = String.format(
@@ -320,12 +339,25 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
                     + "it to be picked up. See https://github.com/elastic/elasticsearch/issues/31399 details.",
                 javaHomeEnvVar
             );
-
             throw new GradleException(exceptionMessage);
         }
         return versionedJavaHome;
     }
 
+    @NotNull
+    private String resolveJavaHomeFromToolChainService(String version) {
+        Property<JavaLanguageVersion> value = objectFactory.property(JavaLanguageVersion.class).value(JavaLanguageVersion.of(version));
+        Provider<JavaLauncher> javaLauncherProvider = toolChainService.launcherFor(
+            javaToolchainSpec -> javaToolchainSpec.getLanguageVersion().value(value)
+        );
+
+        try {
+            return javaLauncherProvider.get().getMetadata().getInstallationPath().getAsFile().getCanonicalPath();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     private static String getJavaHomeEnvVarName(String version) {
         return "JAVA" + version + "_HOME";
     }
@@ -369,7 +401,7 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
         private final JvmVendorSpec expectedVendorSpec;
         private final JavaLanguageVersion expectedJavaLanguageVersion;
 
-        public MetadataBasedToolChainMatcher(JvmInstallationMetadata metadata) {
+        MetadataBasedToolChainMatcher(JvmInstallationMetadata metadata) {
             expectedVendorSpec = JvmVendorSpec.matching(metadata.getVendor().getRawVendor());
             expectedJavaLanguageVersion = JavaLanguageVersion.of(metadata.getLanguageVersion().getMajorVersion());
         }

+ 42 - 0
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AbstractCustomJavaToolchainResolver.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal.toolchain;
+
+import org.gradle.jvm.toolchain.JavaToolchainResolver;
+import org.gradle.jvm.toolchain.JvmVendorSpec;
+import org.gradle.platform.Architecture;
+import org.gradle.platform.OperatingSystem;
+
+abstract class AbstractCustomJavaToolchainResolver implements JavaToolchainResolver {
+
+    static String toOsString(OperatingSystem operatingSystem) {
+        return toOsString(operatingSystem, null);
+    }
+
+    static String toOsString(OperatingSystem operatingSystem, JvmVendorSpec v) {
+        return switch (operatingSystem) {
+            case MAC_OS -> (v == null || v.equals(JvmVendorSpec.ADOPTIUM) == false) ? "macos" : "mac";
+            case LINUX -> "linux";
+            case WINDOWS -> "windows";
+            default -> throw new UnsupportedOperationException("Operating system " + operatingSystem);
+        };
+    }
+
+    static String toArchString(Architecture architecture) {
+        return switch (architecture) {
+            case X86_64 -> "x64";
+            case AARCH64 -> "aarch64";
+            case X86 -> "x86";
+        };
+    }
+
+    protected static boolean anyVendorOr(JvmVendorSpec givenVendor, JvmVendorSpec expectedVendor) {
+        return givenVendor.matches("any") || givenVendor.equals(expectedVendor);
+    }
+}

+ 125 - 0
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolver.java

@@ -0,0 +1,125 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal.toolchain;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.commons.compress.utils.Lists;
+import org.gradle.jvm.toolchain.JavaLanguageVersion;
+import org.gradle.jvm.toolchain.JavaToolchainDownload;
+import org.gradle.jvm.toolchain.JavaToolchainRequest;
+import org.gradle.jvm.toolchain.JvmVendorSpec;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.gradle.jvm.toolchain.JavaToolchainDownload.fromUri;
+
+public abstract class AdoptiumJdkToolchainResolver extends AbstractCustomJavaToolchainResolver {
+
+    // package protected for better testing
+    final Map<AdoptiumVersionRequest, Optional<AdoptiumVersionInfo>> CACHED_SEMVERS = new ConcurrentHashMap<>();
+
+    @Override
+    public Optional<JavaToolchainDownload> resolve(JavaToolchainRequest request) {
+        if (requestIsSupported(request) == false) {
+            return Optional.empty();
+        }
+        AdoptiumVersionRequest versionRequestKey = toVersionRequest(request);
+        Optional<AdoptiumVersionInfo> versionInfo = CACHED_SEMVERS.computeIfAbsent(
+            versionRequestKey,
+            (r) -> resolveAvailableVersion(versionRequestKey)
+        );
+
+        return versionInfo.map(v -> fromUri(resolveDownloadURI(versionRequestKey, v)));
+    }
+
+    private AdoptiumVersionRequest toVersionRequest(JavaToolchainRequest request) {
+        String platform = toOsString(request.getBuildPlatform().getOperatingSystem(), JvmVendorSpec.ADOPTIUM);
+        String arch = toArchString(request.getBuildPlatform().getArchitecture());
+        JavaLanguageVersion javaLanguageVersion = request.getJavaToolchainSpec().getLanguageVersion().get();
+        return new AdoptiumVersionRequest(platform, arch, javaLanguageVersion);
+    }
+
+    private Optional<AdoptiumVersionInfo> resolveAvailableVersion(AdoptiumVersionRequest requestKey) {
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            int languageVersion = requestKey.languageVersion.asInt();
+            URL source = new URL(
+                "https://api.adoptium.net/v3/info/release_versions?architecture="
+                    + requestKey.arch
+                    + "&image_type=jdk&os="
+                    + requestKey.platform
+                    + "&project=jdk&release_type=ga"
+                    + "&version=["
+                    + languageVersion
+                    + ","
+                    + (languageVersion + 1)
+                    + ")"
+            );
+            JsonNode jsonNode = mapper.readTree(source);
+            JsonNode versionsNode = jsonNode.get("versions");
+            return Optional.of(
+                Lists.newArrayList(versionsNode.iterator())
+                    .stream()
+                    .map(node -> toVersionInfo(node))
+                    .sorted(Comparator.comparing(AdoptiumVersionInfo::semver).reversed())
+                    .findFirst()
+                    .get()
+            );
+        } catch (FileNotFoundException e) {
+            // request combo not supported (e.g. aarch64 + windows
+            return Optional.empty();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private AdoptiumVersionInfo toVersionInfo(JsonNode node) {
+        return new AdoptiumVersionInfo(
+            node.get("build").asInt(),
+            node.get("major").asInt(),
+            node.get("minor").asInt(),
+            node.get("openjdk_version").asText(),
+            node.get("security").asInt(),
+            node.get("semver").asText()
+        );
+    }
+
+    private URI resolveDownloadURI(AdoptiumVersionRequest request, AdoptiumVersionInfo versionInfo) {
+        return URI.create(
+            "https://api.adoptium.net/v3/binary/version/jdk-"
+                + versionInfo.openjdkVersion
+                + "/"
+                + request.platform
+                + "/"
+                + request.arch
+                + "/jdk/hotspot/normal/eclipse?project=jdk"
+        );
+    }
+
+    /**
+     * Check if request can be full-filled by this resolver:
+     * 1. vendor must be "any" or adoptium
+     */
+    private boolean requestIsSupported(JavaToolchainRequest request) {
+        return anyVendorOr(request.getJavaToolchainSpec().getVendor().get(), JvmVendorSpec.ADOPTIUM);
+    }
+
+    record AdoptiumVersionInfo(int build, int major, int minor, String openjdkVersion, int security, String semver) {}
+
+    record AdoptiumVersionRequest(String platform, String arch, JavaLanguageVersion languageVersion) {}
+}

+ 82 - 0
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolver.java

@@ -0,0 +1,82 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal.toolchain;
+
+import org.apache.groovy.util.Maps;
+import org.elasticsearch.gradle.VersionProperties;
+import org.gradle.jvm.toolchain.JavaLanguageVersion;
+import org.gradle.jvm.toolchain.JavaToolchainDownload;
+import org.gradle.jvm.toolchain.JavaToolchainRequest;
+import org.gradle.jvm.toolchain.JavaToolchainSpec;
+import org.gradle.jvm.toolchain.JvmVendorSpec;
+import org.gradle.platform.Architecture;
+import org.gradle.platform.BuildPlatform;
+import org.gradle.platform.OperatingSystem;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Optional;
+
+public abstract class ArchivedOracleJdkToolchainResolver extends AbstractCustomJavaToolchainResolver {
+
+    private static final Map<Integer, String> ARCHIVED_BASE_VERSIONS = Maps.of(19, "19.0.2", 18, "18.0.2.1", 17, "17.0.7");
+
+    @Override
+    public Optional<JavaToolchainDownload> resolve(JavaToolchainRequest request) {
+        if (requestIsSupported(request) == false) {
+            return Optional.empty();
+        }
+        Integer majorVersion = request.getJavaToolchainSpec().getLanguageVersion().get().asInt();
+        String baseVersion = ARCHIVED_BASE_VERSIONS.get(majorVersion);
+        if (baseVersion == null) {
+            return Optional.empty();
+        }
+
+        OperatingSystem operatingSystem = request.getBuildPlatform().getOperatingSystem();
+        String extension = operatingSystem.equals(OperatingSystem.WINDOWS) ? "zip" : "tar.gz";
+        String arch = toArchString(request.getBuildPlatform().getArchitecture());
+        String os = toOsString(operatingSystem);
+        return Optional.of(
+            () -> URI.create(
+                "https://download.oracle.com/java/"
+                    + majorVersion
+                    + "/archive/jdk-"
+                    + baseVersion
+                    + "_"
+                    + os
+                    + "-"
+                    + arch
+                    + "_bin."
+                    + extension
+            )
+        );
+    }
+
+    /**
+     * Check if request can be full-filled by this resolver:
+     * 1. language version not matching bundled jdk version
+     * 2. vendor must be any or oracle
+     * 3. Aarch64 windows images are not supported
+     */
+    private boolean requestIsSupported(JavaToolchainRequest request) {
+        JavaToolchainSpec javaToolchainSpec = request.getJavaToolchainSpec();
+        JavaLanguageVersion bundledJdkMajorVersion = JavaLanguageVersion.of(VersionProperties.getBundledJdkMajorVersion());
+        if (javaToolchainSpec.getLanguageVersion().get().equals(bundledJdkMajorVersion)) {
+            return false;
+        }
+        if (anyVendorOr(javaToolchainSpec.getVendor().get(), JvmVendorSpec.ORACLE) == false) {
+            return false;
+        }
+        BuildPlatform buildPlatform = request.getBuildPlatform();
+        Architecture architecture = buildPlatform.getArchitecture();
+        OperatingSystem operatingSystem = buildPlatform.getOperatingSystem();
+        return Architecture.AARCH64 != architecture || OperatingSystem.WINDOWS != operatingSystem;
+    }
+
+}

+ 28 - 0
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/JavaToolChainResolverPlugin.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal.toolchain;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.initialization.Settings;
+import org.gradle.jvm.toolchain.JavaToolchainResolverRegistry;
+
+import javax.inject.Inject;
+
+public abstract class JavaToolChainResolverPlugin implements Plugin<Settings> {
+    @Inject
+    protected abstract JavaToolchainResolverRegistry getToolchainResolverRegistry();
+
+    public void apply(Settings settings) {
+        settings.getPlugins().apply("jvm-toolchain-management");
+        JavaToolchainResolverRegistry registry = getToolchainResolverRegistry();
+        registry.register(OracleOpenJdkToolchainResolver.class);
+        registry.register(AdoptiumJdkToolchainResolver.class);
+        registry.register(ArchivedOracleJdkToolchainResolver.class);
+    }
+}

+ 99 - 0
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolver.java

@@ -0,0 +1,99 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal.toolchain;
+
+import org.elasticsearch.gradle.VersionProperties;
+import org.gradle.jvm.toolchain.JavaLanguageVersion;
+import org.gradle.jvm.toolchain.JavaToolchainDownload;
+import org.gradle.jvm.toolchain.JavaToolchainRequest;
+import org.gradle.jvm.toolchain.JavaToolchainSpec;
+import org.gradle.jvm.toolchain.JvmVendorSpec;
+import org.gradle.platform.Architecture;
+import org.gradle.platform.BuildPlatform;
+import org.gradle.platform.OperatingSystem;
+
+import java.net.URI;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class OracleOpenJdkToolchainResolver extends AbstractCustomJavaToolchainResolver {
+
+    private static final Pattern VERSION_PATTERN = Pattern.compile(
+        "(\\d+)(\\.\\d+\\.\\d+(?:\\.\\d+)?)?\\+(\\d+(?:\\.\\d+)?)(@([a-f0-9]{32}))?"
+    );
+
+    // for testing reasons we keep that a package private field
+    String bundledJdkVersion = VersionProperties.getBundledJdkVersion();
+    JavaLanguageVersion bundledJdkMajorVersion = JavaLanguageVersion.of(VersionProperties.getBundledJdkMajorVersion());
+
+    /**
+     * We need some place to map JavaLanguageVersion to build, minor version etc.
+     * */
+    @Override
+    public Optional<JavaToolchainDownload> resolve(JavaToolchainRequest request) {
+        if (requestIsSupported(request) == false) {
+            return Optional.empty();
+        }
+        Matcher jdkVersionMatcher = VERSION_PATTERN.matcher(bundledJdkVersion);
+        if (jdkVersionMatcher.matches() == false) {
+            throw new IllegalStateException("Unable to parse bundled JDK version " + bundledJdkVersion);
+        }
+        String baseVersion = jdkVersionMatcher.group(1) + (jdkVersionMatcher.group(2) != null ? (jdkVersionMatcher.group(2)) : "");
+        String build = jdkVersionMatcher.group(3);
+        String hash = jdkVersionMatcher.group(5);
+
+        OperatingSystem operatingSystem = request.getBuildPlatform().getOperatingSystem();
+        String extension = operatingSystem.equals(OperatingSystem.WINDOWS) ? "zip" : "tar.gz";
+        String arch = toArchString(request.getBuildPlatform().getArchitecture());
+        String os = toOsString(operatingSystem);
+        return Optional.of(
+            () -> URI.create(
+                "https://download.oracle.com/java/GA/jdk"
+                    + baseVersion
+                    + "/"
+                    + hash
+                    + "/"
+                    + build
+                    + "/GPL/openjdk-"
+                    + baseVersion
+                    + "_"
+                    + os
+                    + "-"
+                    + arch
+                    + "_bin."
+                    + extension
+            )
+        );
+    }
+
+    /**
+     * Check if request can be full-filled by this resolver:
+     * 1. BundledJdkVendor should match openjdk
+     * 2. language version should match bundled jdk version
+     * 3. vendor must be any or oracle
+     * 4. Aarch64 windows images are not supported
+     */
+    private boolean requestIsSupported(JavaToolchainRequest request) {
+        if (VersionProperties.getBundledJdkVendor().toLowerCase().equals("openjdk") == false) {
+            return false;
+        }
+        JavaToolchainSpec javaToolchainSpec = request.getJavaToolchainSpec();
+        if (javaToolchainSpec.getLanguageVersion().get().equals(bundledJdkMajorVersion) == false) {
+            return false;
+        }
+        if (anyVendorOr(javaToolchainSpec.getVendor().get(), JvmVendorSpec.ORACLE) == false) {
+            return false;
+        }
+        BuildPlatform buildPlatform = request.getBuildPlatform();
+        Architecture architecture = buildPlatform.getArchitecture();
+        OperatingSystem operatingSystem = buildPlatform.getOperatingSystem();
+        return Architecture.AARCH64 != architecture || OperatingSystem.WINDOWS != operatingSystem;
+    }
+}

+ 107 - 0
build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AbstractToolchainResolverSpec.groovy

@@ -0,0 +1,107 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal.toolchain
+
+import org.gradle.api.provider.Property
+import org.gradle.jvm.toolchain.JavaLanguageVersion
+import org.gradle.jvm.toolchain.JavaToolchainDownload
+import org.gradle.jvm.toolchain.JavaToolchainRequest
+import org.gradle.jvm.toolchain.JavaToolchainResolver
+import org.gradle.jvm.toolchain.JavaToolchainSpec
+import org.gradle.jvm.toolchain.JvmVendorSpec
+import org.gradle.platform.Architecture
+import org.gradle.platform.BuildPlatform
+import org.gradle.platform.OperatingSystem
+import spock.lang.Specification
+
+import static org.gradle.platform.Architecture.X86_64
+import static org.gradle.platform.OperatingSystem.MAC_OS
+
+abstract class AbstractToolchainResolverSpec extends Specification {
+
+    def "resolves #os #arch #vendor jdk #langVersion"() {
+        given:
+        def resolver = resolverImplementation()
+
+        when:
+        Optional<JavaToolchainDownload> download = resolver.resolve(request(JavaLanguageVersion.of(langVersion), vendor, platform(os, arch)))
+
+        then:
+        download.get().uri == URI.create(expectedUrl)
+        where:
+
+        [langVersion, vendor, os, arch, expectedUrl] << supportedRequests()
+    }
+
+
+    def "does not resolve #os #arch #vendor jdk #langVersion"() {
+        given:
+        def resolver = resolverImplementation()
+
+        when:
+        Optional<JavaToolchainDownload> download = resolver.resolve(request(JavaLanguageVersion.of(langVersion), vendor, platform(os, arch)))
+
+        then:
+        download.isEmpty()
+        where:
+        [langVersion, vendor, os, arch] << unsupportedRequests()
+    }
+
+    abstract JavaToolchainResolver resolverImplementation();
+
+    abstract supportedRequests();
+
+    abstract unsupportedRequests();
+
+    JavaToolchainRequest request(JavaLanguageVersion languageVersion = null,
+                                 JvmVendorSpec vendorSpec = anyVendor(),
+                                 BuildPlatform platform = platform()) {
+
+        JavaToolchainSpec toolchainSpec = Mock()
+        Property<JavaLanguageVersion> languageVersionProperty = Mock()
+        _ * toolchainSpec.getLanguageVersion() >> languageVersionProperty
+        _ * languageVersionProperty.get() >> languageVersion
+
+        Property<JvmVendorSpec> vendorSpecProperty = Mock()
+        _ * vendorSpecProperty.get() >> vendorSpec
+        _ * toolchainSpec.getVendor() >> vendorSpecProperty
+
+        JavaToolchainRequest request = Mock()
+
+        _ * request.getJavaToolchainSpec() >> toolchainSpec
+        _ * request.getBuildPlatform() >> platform
+        return request
+    }
+
+    JvmVendorSpec anyVendor() {
+        return new AnyJvmVendorSpec();
+    }
+
+    BuildPlatform platform(OperatingSystem os = MAC_OS, Architecture arch = X86_64) {
+        return new TestBuildPlatform(operatingSystem: os, architecture: arch)
+    }
+
+
+    static class TestBuildPlatform implements BuildPlatform {
+        OperatingSystem operatingSystem
+        Architecture architecture
+    }
+
+    static class AnyJvmVendorSpec extends JvmVendorSpec {
+        @Override
+        boolean matches(String vendor) {
+            return vendor == "any"
+        }
+
+        @Override
+        String toString() {
+            return "any"
+        }
+    }
+}

+ 83 - 0
build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolverSpec.groovy

@@ -0,0 +1,83 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal.toolchain
+
+import org.gradle.api.services.BuildServiceParameters
+import org.gradle.jvm.toolchain.JavaLanguageVersion
+import org.gradle.jvm.toolchain.JavaToolchainResolver
+import org.gradle.platform.OperatingSystem
+
+import static org.elasticsearch.gradle.internal.toolchain.AbstractCustomJavaToolchainResolver.toArchString
+import static org.elasticsearch.gradle.internal.toolchain.AbstractCustomJavaToolchainResolver.toOsString
+import static org.gradle.jvm.toolchain.JvmVendorSpec.ADOPTIUM
+import static org.gradle.platform.Architecture.AARCH64
+import static org.gradle.platform.Architecture.X86_64
+import static org.gradle.platform.OperatingSystem.LINUX
+import static org.gradle.platform.OperatingSystem.MAC_OS
+import static org.gradle.platform.OperatingSystem.WINDOWS
+
+class AdoptiumJdkToolchainResolverSpec extends AbstractToolchainResolverSpec {
+
+    @Override
+    JavaToolchainResolver resolverImplementation() {
+        def resolver = new AdoptiumJdkToolchainResolver() {
+            @Override
+            BuildServiceParameters.None getParameters() {
+                return null
+            }
+        }
+        supportedRequests().each {
+            def languageVersion = JavaLanguageVersion.of(it[0])
+            def request = new AdoptiumJdkToolchainResolver.AdoptiumVersionRequest(
+                    toOsString(it[2], it[1]),
+                    toArchString(it[3]),
+                    languageVersion);
+            resolver.CACHED_SEMVERS.put(request, Optional.of(new AdoptiumJdkToolchainResolver.AdoptiumVersionInfo(languageVersion.asInt(),
+                    1,
+                    1,
+                    "" + languageVersion.asInt() + ".1.1.1+37",
+                    0, "" + languageVersion.asInt() + ".1.1.1"
+            )))
+
+        }
+        return resolver
+    }
+
+    @Override
+    def supportedRequests() {
+        return [
+                [19, ADOPTIUM, MAC_OS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/mac/x64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [19, ADOPTIUM, LINUX, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/linux/x64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [19, ADOPTIUM, WINDOWS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/windows/x64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [19, ADOPTIUM, MAC_OS, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/mac/aarch64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [19, ADOPTIUM, LINUX, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/linux/aarch64/jdk/hotspot/normal/eclipse?project=jdk"],
+
+                [18, ADOPTIUM, MAC_OS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/mac/x64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [18, ADOPTIUM, LINUX, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/linux/x64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [18, ADOPTIUM, WINDOWS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/windows/x64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [18, ADOPTIUM, MAC_OS, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/mac/aarch64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [18, ADOPTIUM, LINUX, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/linux/aarch64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [17, ADOPTIUM, MAC_OS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/mac/x64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [17, ADOPTIUM, LINUX, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/linux/x64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [17, ADOPTIUM, WINDOWS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/windows/x64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [17, ADOPTIUM, MAC_OS, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/mac/aarch64/jdk/hotspot/normal/eclipse?project=jdk"],
+                [17, ADOPTIUM, LINUX, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/linux/aarch64/jdk/hotspot/normal/eclipse?project=jdk"]
+        ]
+    }
+
+    @Override
+    def unsupportedRequests() {
+        [
+                [19, ADOPTIUM, WINDOWS, AARCH64],
+                [18, ADOPTIUM, WINDOWS, AARCH64],
+                [17, ADOPTIUM, WINDOWS, AARCH64]
+        ]
+    }
+
+}

+ 62 - 0
build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolverSpec.groovy

@@ -0,0 +1,62 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal.toolchain
+
+import org.gradle.api.services.BuildServiceParameters
+import org.gradle.jvm.toolchain.JavaToolchainResolver;
+
+import static org.gradle.jvm.toolchain.JvmVendorSpec.ORACLE
+import static org.gradle.platform.Architecture.AARCH64
+import static org.gradle.platform.Architecture.X86_64
+import static org.gradle.platform.OperatingSystem.LINUX
+import static org.gradle.platform.OperatingSystem.MAC_OS
+import static org.gradle.platform.OperatingSystem.WINDOWS;
+
+class ArchivedOracleJdkToolchainResolverSpec extends AbstractToolchainResolverSpec {
+
+    @Override
+    def supportedRequests() {
+        return [
+                [19, ORACLE, MAC_OS, X86_64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_macos-x64_bin.tar.gz"],
+                [19, ORACLE, MAC_OS, AARCH64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_macos-aarch64_bin.tar.gz"],
+                [19, ORACLE, LINUX, X86_64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_linux-x64_bin.tar.gz"],
+                [19, ORACLE, LINUX, AARCH64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_linux-aarch64_bin.tar.gz"],
+                [19, ORACLE, WINDOWS, X86_64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_windows-x64_bin.zip"],
+
+                [18, ORACLE, MAC_OS, X86_64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_macos-x64_bin.tar.gz"],
+                [18, ORACLE, MAC_OS, AARCH64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_macos-aarch64_bin.tar.gz"],
+                [18, ORACLE, LINUX, X86_64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_linux-x64_bin.tar.gz"],
+                [18, ORACLE, LINUX, AARCH64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_linux-aarch64_bin.tar.gz"],
+                [18, ORACLE, WINDOWS, X86_64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_windows-x64_bin.zip"],
+
+                [17, ORACLE, MAC_OS, X86_64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_macos-x64_bin.tar.gz"],
+                [17, ORACLE, MAC_OS, AARCH64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_macos-aarch64_bin.tar.gz"],
+                [17, ORACLE, LINUX, X86_64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_linux-x64_bin.tar.gz"],
+                [17, ORACLE, LINUX, AARCH64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_linux-aarch64_bin.tar.gz"],
+                [17, ORACLE, WINDOWS, X86_64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_windows-x64_bin.zip"]
+        ]
+    }
+
+    def unsupportedRequests() {
+        [
+                [19, ORACLE, WINDOWS, AARCH64],
+                [18, ORACLE, WINDOWS, AARCH64],
+                [17, ORACLE, WINDOWS, AARCH64]
+        ]
+    }
+
+    JavaToolchainResolver resolverImplementation() {
+        new ArchivedOracleJdkToolchainResolver() {
+            @Override
+            BuildServiceParameters.None getParameters() {
+                return null
+            }
+        }
+    }
+}

+ 51 - 0
build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolverSpec.groovy

@@ -0,0 +1,51 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal.toolchain
+
+
+import org.gradle.api.services.BuildServiceParameters
+import org.gradle.jvm.toolchain.JavaLanguageVersion
+import static org.gradle.jvm.toolchain.JvmVendorSpec.ORACLE
+import static org.gradle.platform.Architecture.*
+import static org.gradle.platform.OperatingSystem.*
+
+class OracleOpenJdkToolchainResolverSpec extends AbstractToolchainResolverSpec {
+
+    OracleOpenJdkToolchainResolver resolverImplementation() {
+        var toolChain = new OracleOpenJdkToolchainResolver() {
+            @Override
+            BuildServiceParameters.None getParameters() {
+                return null
+            }
+        }
+        toolChain.bundledJdkVersion = "20+36@bdc68b4b9cbc4ebcb30745c85038d91d"
+        toolChain.bundledJdkMajorVersion = JavaLanguageVersion.of(20)
+        toolChain
+    }
+
+    def supportedRequests() {
+        [[20, ORACLE, MAC_OS, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_macos-x64_bin.tar.gz"],
+         [20, ORACLE, MAC_OS, AARCH64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_macos-aarch64_bin.tar.gz"],
+         [20, ORACLE, LINUX, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_linux-x64_bin.tar.gz"],
+         [20, ORACLE, LINUX, AARCH64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_linux-aarch64_bin.tar.gz"],
+         [20, ORACLE, WINDOWS, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_windows-x64_bin.zip"],
+         [20, anyVendor(), MAC_OS, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_macos-x64_bin.tar.gz"],
+         [20, anyVendor(), MAC_OS, AARCH64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_macos-aarch64_bin.tar.gz"],
+         [20, anyVendor(), LINUX, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_linux-x64_bin.tar.gz"],
+         [20, anyVendor(), LINUX, AARCH64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_linux-aarch64_bin.tar.gz"],
+         [20, anyVendor(), WINDOWS, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_windows-x64_bin.zip"]]
+    }
+
+    def unsupportedRequests() {
+        [
+                [20, ORACLE, WINDOWS, AARCH64]
+        ]
+    }
+
+}

+ 4 - 0
build-tools/settings.gradle

@@ -5,6 +5,10 @@
  * in compliance with, at your election, the Elastic License 2.0 or the Server
  * Side Public License, v 1.
  */
+pluginManagement {
+    includeBuild "../build-conventions"
+}
+
 include 'reaper'
 
 dependencyResolutionManagement {

+ 7 - 0
build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java

@@ -30,6 +30,10 @@ public class VersionProperties {
         return lucene;
     }
 
+    public static String getBundledJdkMajorVersion() {
+        return bundledJdkMajorVersion;
+    }
+
     public static String getBundledJdkVersion() {
         return bundledJdkVersion;
     }
@@ -45,7 +49,9 @@ public class VersionProperties {
     private static final String elasticsearch;
     private static final String lucene;
     private static final String bundledJdkVersion;
+    private static final String bundledJdkMajorVersion;
     private static final String bundledJdkVendor;
+
     private static final Map<String, String> versions = new HashMap<String, String>();
 
     static {
@@ -54,6 +60,7 @@ public class VersionProperties {
         lucene = props.getProperty("lucene");
         bundledJdkVendor = props.getProperty("bundled_jdk_vendor");
         bundledJdkVersion = props.getProperty("bundled_jdk");
+        bundledJdkMajorVersion = bundledJdkVersion.split("[.+]")[0];
 
         for (String property : props.stringPropertyNames()) {
             versions.put(property, props.getProperty(property));

+ 0 - 2
build.gradle

@@ -19,8 +19,6 @@ import org.elasticsearch.gradle.internal.info.BuildParams
 import org.elasticsearch.gradle.util.GradleUtils
 import org.gradle.plugins.ide.eclipse.model.AccessRule
 import org.gradle.plugins.ide.eclipse.model.ProjectDependency
-import org.gradle.util.internal.DistributionLocator
-import org.gradle.util.GradleVersion
 
 import java.nio.file.Files
 

+ 25 - 4
settings.gradle

@@ -1,18 +1,23 @@
+import org.elasticsearch.gradle.internal.toolchain.OracleOpenJdkToolchainResolver
+import org.elasticsearch.gradle.internal.toolchain.ArchivedOracleJdkToolchainResolver
+import org.elasticsearch.gradle.internal.toolchain.AdoptiumJdkToolchainResolver
+
 pluginManagement {
   repositories {
     mavenCentral()
     gradlePluginPortal()
   }
+
+  includeBuild "build-conventions"
+  includeBuild "build-tools"
+  includeBuild "build-tools-internal"
 }
 
 plugins {
   id "com.gradle.enterprise" version "3.12.6"
+  id 'elasticsearch.java-toolchain'
 }
 
-includeBuild "build-conventions"
-includeBuild "build-tools"
-includeBuild "build-tools-internal"
-
 rootProject.name = "elasticsearch"
 
 dependencyResolutionManagement {
@@ -23,6 +28,22 @@ dependencyResolutionManagement {
   }
 }
 
+toolchainManagement {
+  jvm {
+    javaRepositories {
+      repository('bundledOracleOpendJdk') {
+        resolverClass = OracleOpenJdkToolchainResolver
+      }
+      repository('adoptiumJdks') {
+        resolverClass = AdoptiumJdkToolchainResolver
+      }
+      repository('archivedOracleJdks') {
+        resolverClass = ArchivedOracleJdkToolchainResolver
+      }
+    }
+  }
+}
+
 List projects = [
   'rest-api-spec',
   'docs',

+ 1 - 1
test/test-clusters/build.gradle

@@ -14,5 +14,5 @@ dependencies {
 }
 
 tasks.named("processResources").configure {
-  from(new File(Util.locateElasticsearchWorkspace(gradle), "build-tools-internal/version.properties"))
+  from(new File(Util.locateElasticsearchWorkspace(project.gradle), "build-tools-internal/version.properties"))
 }