This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-toolchains-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new e22b3cf  [MTOOLCHAINS-49] Automatic discovery of JDK toolchains (#14)
e22b3cf is described below

commit e22b3cf1d8de62fdea8673134d9e172ec454f449
Author: Guillaume Nodet <gno...@gmail.com>
AuthorDate: Wed Mar 20 09:00:12 2024 +0100

    [MTOOLCHAINS-49] Automatic discovery of JDK toolchains (#14)
---
 pom.xml                                            |  28 ++
 .../jdk/DisplayDiscoveredJdkToolchainsMojo.java    |  75 ++++
 .../jdk/GenerateJdkToolchainsXmlMojo.java          |  75 ++++
 .../toolchain/jdk/SelectJdkToolchainMojo.java      | 265 ++++++++++++
 .../plugins/toolchain/jdk/ToolchainDiscoverer.java | 465 +++++++++++++++++++++
 src/site/apt/index.apt.vm                          |  21 +-
 src/site/apt/toolchains/discovery.apt.vm           | 147 +++++++
 src/site/apt/toolchains/jdk.apt.vm                 |   1 +
 src/site/site.xml                                  |   4 +
 .../toolchain/jdk/ToolchainDiscovererTest.java     |  54 +++
 10 files changed, 1133 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index 3bc53f3..59c0165 100644
--- a/pom.xml
+++ b/pom.xml
@@ -90,6 +90,28 @@ under the License.
       <artifactId>maven-plugin-annotations</artifactId>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+      <version>4.0.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-xml</artifactId>
+      <version>3.0.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.36</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-api</artifactId>
+      <version>5.10.2</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -114,5 +136,11 @@ under the License.
         </plugin>
       </plugins>
     </pluginManagement>
+    <plugins>
+      <plugin>
+        <groupId>org.eclipse.sisu</groupId>
+        <artifactId>sisu-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
   </build>
 </project>
diff --git 
a/src/main/java/org/apache/maven/plugins/toolchain/jdk/DisplayDiscoveredJdkToolchainsMojo.java
 
b/src/main/java/org/apache/maven/plugins/toolchain/jdk/DisplayDiscoveredJdkToolchainsMojo.java
new file mode 100644
index 0000000..bf3b46c
--- /dev/null
+++ 
b/src/main/java/org/apache/maven/plugins/toolchain/jdk/DisplayDiscoveredJdkToolchainsMojo.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.toolchain.jdk;
+
+import javax.inject.Inject;
+
+import java.util.List;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.toolchain.model.PersistedToolchains;
+import org.apache.maven.toolchain.model.ToolchainModel;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+
+import static java.util.Comparator.comparing;
+import static 
org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.SORTED_PROVIDES;
+
+/**
+ * Discover the JDK toolchains and print them to the console.
+ */
+@Mojo(name = "display-discovered-jdk-toolchains", requiresProject = false)
+public class DisplayDiscoveredJdkToolchainsMojo extends AbstractMojo {
+
+    /**
+     * Comparator used to sort JDK toolchains for selection.
+     * This property is a comma separated list of values which may contains:
+     * <ul>
+     * <li>{@code lts}: prefer JDK with LTS version</li>
+     * <li>{@code current}: prefer the current JDK</li>
+     * <li>{@code env}: prefer JDKs defined using {@code JAVA\{xx\}_HOME} 
environment variables</li>
+     * <li>{@code version}: prefer JDK with higher versions</li>
+     * <li>{@code vendor}: order JDK by vendor name (usually as a last 
comparator to ensure a stable order)</li>
+     * </ul>
+     */
+    @Parameter(property = "toolchain.jdk.comparator", defaultValue = 
"lts,current,env,version,vendor")
+    String comparator;
+
+    /**
+     * Toolchain discoverer
+     */
+    @Inject
+    ToolchainDiscoverer discoverer;
+
+    @Override
+    public void execute() {
+        PersistedToolchains toolchains = 
discoverer.discoverToolchains(comparator);
+        List<ToolchainModel> models = toolchains.getToolchains();
+        getLog().info("Discovered " + models.size() + " JDK toolchains:");
+        for (ToolchainModel model : models) {
+            getLog().info("  - "
+                    + ((Xpp3Dom) 
model.getConfiguration()).getChild("jdkHome").getValue());
+            getLog().info("    provides:");
+            model.getProvides().entrySet().stream()
+                    .sorted(comparing(e -> 
SORTED_PROVIDES.indexOf(e.getKey().toString())))
+                    .forEach(e -> getLog().info("      " + e.getKey() + ": " + 
e.getValue()));
+        }
+    }
+}
diff --git 
a/src/main/java/org/apache/maven/plugins/toolchain/jdk/GenerateJdkToolchainsXmlMojo.java
 
b/src/main/java/org/apache/maven/plugins/toolchain/jdk/GenerateJdkToolchainsXmlMojo.java
new file mode 100644
index 0000000..450eb95
--- /dev/null
+++ 
b/src/main/java/org/apache/maven/plugins/toolchain/jdk/GenerateJdkToolchainsXmlMojo.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.toolchain.jdk;
+
+import javax.inject.Inject;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.toolchain.model.PersistedToolchains;
+import org.apache.maven.toolchain.model.io.xpp3.MavenToolchainsXpp3Writer;
+
+/**
+ * Run the JDK toolchain discovery mechanism and generates a toolchains XML.
+ */
+@Mojo(name = "generate-jdk-toolchains-xml", requiresProject = false)
+public class GenerateJdkToolchainsXmlMojo extends AbstractMojo {
+
+    /**
+     * The path and name pf the toolchain XML file that will be generated.
+     * If not provided, the XML will be written to the standard output.
+     */
+    @Parameter(property = "toolchain.file")
+    String file;
+
+    /**
+     * Toolchain discoverer
+     */
+    @Inject
+    ToolchainDiscoverer discoverer;
+
+    @Override
+    public void execute() throws MojoFailureException {
+        try {
+            PersistedToolchains toolchains = discoverer.discoverToolchains();
+            if (file != null) {
+                Path file = Paths.get(this.file).toAbsolutePath();
+                Files.createDirectories(file.getParent());
+                try (Writer writer = Files.newBufferedWriter(file)) {
+                    new MavenToolchainsXpp3Writer().write(writer, toolchains);
+                }
+            } else {
+                StringWriter writer = new StringWriter();
+                new MavenToolchainsXpp3Writer().write(writer, toolchains);
+                System.out.println(writer);
+            }
+        } catch (IOException e) {
+            throw new MojoFailureException("Unable to generate 
toolchains.xml", e);
+        }
+    }
+}
diff --git 
a/src/main/java/org/apache/maven/plugins/toolchain/jdk/SelectJdkToolchainMojo.java
 
b/src/main/java/org/apache/maven/plugins/toolchain/jdk/SelectJdkToolchainMojo.java
new file mode 100644
index 0000000..02efff9
--- /dev/null
+++ 
b/src/main/java/org/apache/maven/plugins/toolchain/jdk/SelectJdkToolchainMojo.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.toolchain.jdk;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.toolchain.MisconfiguredToolchainException;
+import org.apache.maven.toolchain.RequirementMatcherFactory;
+import org.apache.maven.toolchain.ToolchainFactory;
+import org.apache.maven.toolchain.ToolchainManagerPrivate;
+import org.apache.maven.toolchain.ToolchainPrivate;
+import org.apache.maven.toolchain.model.PersistedToolchains;
+import org.apache.maven.toolchain.model.ToolchainModel;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+
+import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.ENV;
+import static 
org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_NAME;
+import static 
org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_VERSION;
+import static 
org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VENDOR;
+import static 
org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VERSION;
+
+/**
+ * Discover JDK toolchains and select a matching one.
+ */
+@Mojo(name = "select-jdk-toolchain", defaultPhase = LifecyclePhase.VALIDATE)
+public class SelectJdkToolchainMojo extends AbstractMojo {
+
+    public static final String TOOLCHAIN_TYPE_JDK = "jdk";
+
+    /** Jdk usage mode */
+    public enum JdkMode {
+        /** always ignore the current JDK */
+        Never,
+        /** to not use a toolchain if the toolchains that would be selected is 
the current JDK */
+        IfSame,
+        /** favor the current JDK if it matches the requirements */
+        IfMatch
+    }
+
+    /**
+     * The version constraint for the JDK toolchain to select.
+     */
+    @Parameter(property = "toolchain.jdk.version")
+    private String version;
+
+    /**
+     * The runtime name constraint for the JDK toolchain to select.
+     */
+    @Parameter(property = "toolchain.jdk.runtime.name")
+    private String runtimeName;
+
+    /**
+     * The runtime version constraint for the JDK toolchain to select.
+     */
+    @Parameter(property = "toolchain.jdk.runtime.version")
+    private String runtimeVersion;
+
+    /**
+     * The vendor constraint for the JDK toolchain to select.
+     */
+    @Parameter(property = "toolchain.jdk.vendor")
+    private String vendor;
+
+    /**
+     * The env constraint for the JDK toolchain to select.
+     * To match the constraint, an environment variable with the given name 
must point to the JDK.
+     * For example, if you define {@code JAVA11_HOME=~/jdks/my-jdk-11.0.1}, 
you can specify
+     * {@code env=JAVA11_HOME} to match the given JDK.
+     */
+    @Parameter(property = "toolchain.jdk.env")
+    private String env;
+
+    /**
+     * The matching mode, either {@code IfMatch} (the default), {@code 
IfSame}, or {@code Never}.
+     * If {@code IfMatch} is used, a toolchain will not be selected if the 
running JDK does
+     * match the provided constraints. This is the default and provides better 
performances as it
+     * avoids forking a different process when it's not required. The {@code 
IfSame} avoids
+     * selecting a toolchain if the toolchain selected is exactly the same as 
the running JDK.
+     * THe {@code Never} option will always select the toolchain.
+     */
+    @Parameter(property = "toolchain.jdk.mode", defaultValue = "IfMatch")
+    private JdkMode useJdk = JdkMode.IfMatch;
+
+    /**
+     * Automatically discover JDK toolchains using the built-in heuristic.
+     * The default value is {@code true}.
+     */
+    @Parameter(property = "toolchain.jdk.discover", defaultValue = "true")
+    private boolean discoverToolchains = true;
+
+    /**
+     * Comparator used to sort JDK toolchains for selection.
+     * This property is a comma separated list of values which may contains:
+     * <ul>
+     * <li>{@code lts}: prefer JDK with LTS version</li>
+     * <li>{@code current}: prefer the current JDK</li>
+     * <li>{@code env}: prefer JDKs defined using {@code JAVA\{xx\}_HOME} 
environment variables</li>
+     * <li>{@code version}: prefer JDK with higher versions</li>
+     * <li>{@code vendor}: order JDK by vendor name (usually as a last 
comparator to ensure a stable order)</li>
+     * </ul>
+     */
+    @Parameter(property = "toolchain.jdk.comparator", defaultValue = 
"lts,current,env,version,vendor")
+    private String comparator;
+
+    /**
+     * Toolchain manager
+     */
+    @Inject
+    private ToolchainManagerPrivate toolchainManager;
+
+    /**
+     * Toolchain factory
+     */
+    @Inject
+    @Named(TOOLCHAIN_TYPE_JDK)
+    ToolchainFactory factory;
+
+    /**
+     * The current build session instance. This is used for toolchain manager 
API calls.
+     */
+    @Inject
+    private MavenSession session;
+
+    /**
+     * Toolchain discoverer
+     */
+    @Inject
+    ToolchainDiscoverer discoverer;
+
+    @Override
+    public void execute() throws MojoFailureException {
+        try {
+            doExecute();
+        } catch (MisconfiguredToolchainException e) {
+            throw new MojoFailureException("Unable to select toolchain: " + e, 
e);
+        }
+    }
+
+    private void doExecute() throws MisconfiguredToolchainException, 
MojoFailureException {
+        if (version == null && runtimeName == null && runtimeVersion == null 
&& vendor == null && env == null) {
+            return;
+        }
+
+        Map<String, String> requirements = new HashMap<>();
+        Optional.ofNullable(version).ifPresent(v -> requirements.put(VERSION, 
v));
+        Optional.ofNullable(runtimeName).ifPresent(v -> 
requirements.put(RUNTIME_NAME, v));
+        Optional.ofNullable(runtimeVersion).ifPresent(v -> 
requirements.put(RUNTIME_VERSION, v));
+        Optional.ofNullable(vendor).ifPresent(v -> requirements.put(VENDOR, 
v));
+        Optional.ofNullable(env).ifPresent(v -> requirements.put(ENV, v));
+
+        ToolchainModel currentJdkToolchainModel =
+                discoverer.getCurrentJdkToolchain().orElse(null);
+        ToolchainPrivate currentJdkToolchain =
+                currentJdkToolchainModel != null ? 
factory.createToolchain(currentJdkToolchainModel) : null;
+
+        if (useJdk == JdkMode.IfMatch && currentJdkToolchain != null && 
matches(currentJdkToolchain, requirements)) {
+            getLog().info("Not using an external toolchain as the current JDK 
matches the requirements.");
+            return;
+        }
+
+        ToolchainPrivate toolchain = 
Stream.of(toolchainManager.getToolchainsForType(TOOLCHAIN_TYPE_JDK, session))
+                .filter(tc -> matches(tc, requirements))
+                .findFirst()
+                .orElse(null);
+        if (toolchain != null) {
+            getLog().info("Found matching JDK toolchain: " + toolchain);
+        }
+
+        if (toolchain == null && discoverToolchains) {
+            getLog().debug("No matching toolchains configured, trying to 
discover JDK toolchains");
+            PersistedToolchains persistedToolchains = 
discoverer.discoverToolchains(comparator);
+            getLog().debug("Discovered " + 
persistedToolchains.getToolchains().size() + " JDK toolchains");
+
+            for (ToolchainModel tcm : persistedToolchains.getToolchains()) {
+                ToolchainPrivate tc = factory.createToolchain(tcm);
+                if (tc != null && matches(tc, requirements)) {
+                    toolchain = tc;
+                    getLog().debug("Discovered matching JDK toolchain: " + 
toolchain);
+                    break;
+                }
+            }
+        }
+
+        if (toolchain == null) {
+            throw new MojoFailureException(
+                    "Cannot find matching toolchain definitions for the 
following toolchain types:" + requirements
+                            + System.lineSeparator()
+                            + "Define the required toolchains in your 
~/.m2/toolchains.xml file.");
+        }
+
+        if (useJdk == JdkMode.IfSame
+                && currentJdkToolchain != null
+                && Objects.equals(getJdkHome(currentJdkToolchain), 
getJdkHome(toolchain))) {
+            getLog().debug("Not using an external toolchain as the current JDK 
has been selected.");
+            return;
+        }
+
+        toolchainManager.storeToolchainToBuildContext(toolchain, session);
+        getLog().debug("Found matching JDK toolchain: " + toolchain);
+    }
+
+    private boolean matches(ToolchainPrivate tc, Map<String, String> 
requirements) {
+        ToolchainModel model = tc.getModel();
+        for (Map.Entry<String, String> req : requirements.entrySet()) {
+            String key = req.getKey();
+            String reqVal = req.getValue();
+            String tcVal = model.getProvides().getProperty(key);
+            if (tcVal == null) {
+                getLog().debug("Toolchain " + tc + " is missing required 
property: " + key);
+                return false;
+            }
+            if (!matches(key, reqVal, tcVal)) {
+                getLog().debug("Toolchain " + tc + " doesn't match required 
property: " + key);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean matches(String key, String reqVal, String tcVal) {
+        switch (key) {
+            case VERSION:
+                return 
RequirementMatcherFactory.createVersionMatcher(tcVal).matches(reqVal);
+            case ENV:
+                return reqVal.matches("(.*,|^)\\Q" + tcVal + "\\E(,.*|$)");
+            default:
+                return 
RequirementMatcherFactory.createExactMatcher(tcVal).matches(reqVal);
+        }
+    }
+
+    private String getJdkHome(ToolchainPrivate toolchain) {
+        return ((Xpp3Dom) toolchain.getModel().getConfiguration())
+                .getChild("jdkHome")
+                .getValue();
+    }
+}
diff --git 
a/src/main/java/org/apache/maven/plugins/toolchain/jdk/ToolchainDiscoverer.java 
b/src/main/java/org/apache/maven/plugins/toolchain/jdk/ToolchainDiscoverer.java
new file mode 100644
index 0000000..f138565
--- /dev/null
+++ 
b/src/main/java/org/apache/maven/plugins/toolchain/jdk/ToolchainDiscoverer.java
@@ -0,0 +1,465 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.toolchain.jdk;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.maven.toolchain.model.PersistedToolchains;
+import org.apache.maven.toolchain.model.ToolchainModel;
+import org.apache.maven.toolchain.model.io.xpp3.MavenToolchainsXpp3Reader;
+import org.apache.maven.toolchain.model.io.xpp3.MavenToolchainsXpp3Writer;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Comparator.comparing;
+import static 
org.apache.maven.plugins.toolchain.jdk.SelectJdkToolchainMojo.TOOLCHAIN_TYPE_JDK;
+
+/**
+ * Toolchain discoverer service
+ */
+@Named
+@Singleton
+public class ToolchainDiscoverer {
+
+    public static final String JAVA = "java.";
+    public static final String VERSION = "version";
+    public static final String RUNTIME_NAME = "runtime.name";
+    public static final String RUNTIME_VERSION = "runtime.version";
+    public static final String VENDOR = "vendor";
+    public static final String VENDOR_VERSION = "vendor.version";
+    public static final String[] PROPERTIES = {VERSION, RUNTIME_NAME, 
RUNTIME_VERSION, VENDOR, VENDOR_VERSION};
+
+    public static final String CURRENT = "current";
+    public static final String ENV = "env";
+    public static final String LTS = "lts";
+
+    public static final List<String> SORTED_PROVIDES = 
Collections.unmodifiableList(
+            Arrays.asList(VERSION, RUNTIME_NAME, RUNTIME_VERSION, VENDOR, 
VENDOR_VERSION, CURRENT, LTS, ENV));
+
+    public static final String DISCOVERED_TOOLCHAINS_CACHE_XML = 
".m2/discovered-toolchains-cache.xml";
+
+    public static final String JDK_HOME = "jdkHome";
+    public static final String JAVA_HOME = "java.home";
+
+    private static final String COMMA = ",";
+    public static final String USER_HOME = "user.home";
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private volatile Map<Path, ToolchainModel> cache;
+    private volatile boolean cacheModified;
+    private volatile Set<Path> foundJdks;
+
+    /**
+     * Build the model for the current JDK toolchain
+     */
+    public Optional<ToolchainModel> getCurrentJdkToolchain() {
+        Path currentJdkHome = 
getCanonicalPath(Paths.get(System.getProperty(JAVA_HOME)));
+        if (!hasJavaC(currentJdkHome)) {
+            // in case the current JVM is not a JDK
+            return Optional.empty();
+        }
+        ToolchainModel model = new ToolchainModel();
+        model.setType(TOOLCHAIN_TYPE_JDK);
+        Stream.of(PROPERTIES).forEach(k -> {
+            String v = System.getProperty(JAVA + k);
+            if (v != null) {
+                model.addProvide(k, v);
+            }
+        });
+        model.addProvide(CURRENT, "true");
+        Xpp3Dom config = new Xpp3Dom("configuration");
+        Xpp3Dom jdkHome = new Xpp3Dom(JDK_HOME);
+        jdkHome.setValue(currentJdkHome.toString());
+        config.addChild(jdkHome);
+        model.setConfiguration(config);
+        return Optional.of(model);
+    }
+
+    public PersistedToolchains discoverToolchains() {
+        return discoverToolchains(LTS + COMMA + VERSION + COMMA + VENDOR);
+    }
+
+    /**
+     * Returns a PersistedToolchains object containing a list of discovered 
toolchains,
+     * never <code>null</code>.
+     */
+    public PersistedToolchains discoverToolchains(String comparator) {
+        try {
+            Set<Path> jdks = findJdks();
+            log.info("Found " + jdks.size() + " possible jdks: " + jdks);
+            readCache();
+            Map<Path, Map<String, String>> flags = new HashMap<>();
+            Path currentJdkHome = 
getCanonicalPath(Paths.get(System.getProperty(JAVA_HOME)));
+            flags.computeIfAbsent(currentJdkHome, p -> new 
HashMap<>()).put(CURRENT, "true");
+            // check environment variables for JAVA{xx}_HOME
+            System.getenv().entrySet().stream()
+                    .filter(e -> e.getKey().startsWith("JAVA") && 
e.getKey().endsWith("_HOME"))
+                    .forEach(e -> {
+                        Path path = getCanonicalPath(Paths.get(e.getValue()));
+                        Map<String, String> f = flags.computeIfAbsent(path, p 
-> new HashMap<>());
+                        String val = f.getOrDefault(ENV, "");
+                        f.put(ENV, (val.isEmpty() ? "" : val + ",") + 
e.getKey());
+                    });
+
+            List<ToolchainModel> tcs = jdks.parallelStream()
+                    .map(s -> {
+                        ToolchainModel tc = getToolchainModel(s);
+                        flags.getOrDefault(s, Collections.emptyMap())
+                                .forEach((k, v) -> 
tc.getProvides().setProperty(k, v));
+                        String version = tc.getProvides().getProperty(VERSION);
+                        if (isLts(version)) {
+                            tc.getProvides().setProperty(LTS, "true");
+                        }
+                        return tc;
+                    })
+                    .sorted(getToolchainModelComparator(comparator))
+                    .collect(Collectors.toList());
+            writeCache();
+            PersistedToolchains ps = new PersistedToolchains();
+            ps.setToolchains(tcs);
+            return ps;
+        } catch (Exception e) {
+            if (log.isDebugEnabled()) {
+                log.warn("Error discovering toolchains: " + e, e);
+            } else {
+                log.warn("Error discovering toolchains (enable debug level for 
more information): " + e);
+            }
+            return new PersistedToolchains();
+        }
+    }
+
+    private static boolean isLts(String version) {
+        return Stream.of("1.8", "8", "11", "17", "21", "25")
+                .anyMatch(v -> version.equals(v) || version.startsWith(v + 
"."));
+    }
+
+    private synchronized void readCache() {
+        if (cache == null) {
+            try {
+                cache = new ConcurrentHashMap<>();
+                cacheModified = false;
+                Path cacheFile = getCacheFile();
+                if (Files.isRegularFile(cacheFile)) {
+                    try (Reader r = Files.newBufferedReader(cacheFile)) {
+                        PersistedToolchains pt = new 
MavenToolchainsXpp3Reader().read(r, false);
+                        cache = pt.getToolchains().stream()
+                                // Remove stale entries
+                                .filter(tc -> {
+                                    // If the bin/java executable is not 
available anymore, remove this TC
+                                    if (!hasJavaC(getJdkHome(tc))) {
+                                        cacheModified = true;
+                                        return false;
+                                    } else {
+                                        return true;
+                                    }
+                                })
+                                
.collect(Collectors.toConcurrentMap(this::getJdkHome, Function.identity()));
+                    }
+                }
+            } catch (IOException | XmlPullParserException e) {
+                log.debug("Error reading toolchains cache: " + e, e);
+            }
+        }
+    }
+
+    private synchronized void writeCache() {
+        if (cacheModified) {
+            try {
+                Path cacheFile = getCacheFile();
+                Files.createDirectories(cacheFile.getParent());
+                try (Writer w = Files.newBufferedWriter(cacheFile)) {
+                    PersistedToolchains pt = new PersistedToolchains();
+                    pt.setToolchains(cache.values().stream()
+                            .map(tc -> {
+                                ToolchainModel model = tc.clone();
+                                // Remove transient information
+                                model.getProvides().remove(CURRENT);
+                                model.getProvides().remove(ENV);
+                                return model;
+                            })
+                            .sorted(version().thenComparing(vendor()))
+                            .collect(Collectors.toList()));
+                    new MavenToolchainsXpp3Writer().write(w, pt);
+                }
+            } catch (IOException e) {
+                log.debug("Error writing toolchains cache: " + e, e);
+            }
+            cacheModified = false;
+        }
+    }
+
+    ToolchainModel getToolchainModel(Path jdk) {
+        ToolchainModel model = cache.get(jdk);
+        if (model == null) {
+            model = doGetToolchainModel(jdk);
+            cache.put(jdk, model);
+            cacheModified = true;
+        }
+        return model;
+    }
+
+    private static Path getCacheFile() {
+        return 
Paths.get(System.getProperty(USER_HOME)).resolve(DISCOVERED_TOOLCHAINS_CACHE_XML);
+    }
+
+    public Path getJdkHome(ToolchainModel toolchain) {
+        Xpp3Dom dom = (Xpp3Dom) toolchain.getConfiguration();
+        Xpp3Dom javahome = dom != null ? dom.getChild(JDK_HOME) : null;
+        String jdk = javahome != null ? javahome.getValue() : null;
+        return Paths.get(Objects.requireNonNull(jdk));
+    }
+
+    ToolchainModel doGetToolchainModel(Path jdk) {
+        Path java = jdk.resolve("bin").resolve("java");
+        if (!Files.exists(java)) {
+            java = jdk.resolve("bin").resolve("java.exe");
+            if (!Files.exists(java)) {
+                log.debug("JDK toolchain discovered at " + jdk
+                        + " will be ignored: unable to find bin/java or 
bin\\java.exe");
+                return null;
+            }
+        }
+        if (!java.toFile().canExecute()) {
+            log.debug("JDK toolchain discovered at " + jdk
+                    + " will be ignored: the bin/java or bin\\java.exe is not 
executable");
+            return null;
+        }
+        List<String> lines;
+        try {
+            Path temp = Files.createTempFile("jdk-opts-", ".out");
+            try {
+                new ProcessBuilder()
+                        .command(java.toString(), "-XshowSettings:properties", 
"-version")
+                        .redirectError(temp.toFile())
+                        .start()
+                        .waitFor();
+                lines = Files.readAllLines(temp);
+            } finally {
+                Files.delete(temp);
+            }
+        } catch (IOException | InterruptedException e) {
+            log.debug("JDK toolchain discovered at " + jdk + " will be 
ignored: error executing java: " + e);
+            return null;
+        }
+
+        Map<String, String> properties = new LinkedHashMap<>();
+        Stream.of(PROPERTIES).forEach(name -> {
+            lines.stream()
+                    .filter(l -> l.contains(JAVA + name))
+                    .map(l -> l.replaceFirst(".*=\\s*(.*)", "$1"))
+                    .findFirst()
+                    .ifPresent(value -> properties.put(name, value));
+        });
+        if (!properties.containsKey(VERSION)) {
+            log.debug("JDK toolchain discovered at " + jdk + " will be 
ignored: could not obtain " + JAVA + VERSION);
+            return null;
+        }
+
+        ToolchainModel model = new ToolchainModel();
+        model.setType(TOOLCHAIN_TYPE_JDK);
+        properties.forEach(model::addProvide);
+        Xpp3Dom configuration = new Xpp3Dom("configuration");
+        Xpp3Dom jdkHome = new Xpp3Dom(JDK_HOME);
+        jdkHome.setValue(jdk.toString());
+        configuration.addChild(jdkHome);
+        model.setConfiguration(configuration);
+        return model;
+    }
+
+    private static Path getCanonicalPath(Path path) {
+        try {
+            return path.toRealPath();
+        } catch (IOException e) {
+            return 
getCanonicalPath(path.getParent()).resolve(path.getFileName());
+        }
+    }
+
+    Comparator<ToolchainModel> getToolchainModelComparator(String comparator) {
+        Comparator<ToolchainModel> c = null;
+        for (String part : comparator.split(COMMA)) {
+            c = c == null ? getComparator(part) : 
c.thenComparing(getComparator(part));
+        }
+        return c;
+    }
+
+    private Comparator<ToolchainModel> getComparator(String part) {
+        switch (part.trim().toLowerCase(Locale.ROOT)) {
+            case LTS:
+                return lts();
+            case VENDOR:
+                return vendor();
+            case ENV:
+                return env();
+            case CURRENT:
+                return current();
+            case VERSION:
+                return version();
+            default:
+                throw new IllegalArgumentException("Unsupported comparator: " 
+ part
+                        + ". Supported comparators are: vendor, env, current, 
lts and version.");
+        }
+    }
+
+    Comparator<ToolchainModel> lts() {
+        return comparing((ToolchainModel tc) -> 
tc.getProvides().containsKey(LTS) ? -1 : +1);
+    }
+
+    Comparator<ToolchainModel> vendor() {
+        return comparing((ToolchainModel tc) -> 
tc.getProvides().getProperty(VENDOR));
+    }
+
+    Comparator<ToolchainModel> env() {
+        return comparing((ToolchainModel tc) -> 
tc.getProvides().containsKey(ENV) ? -1 : +1);
+    }
+
+    Comparator<ToolchainModel> current() {
+        return comparing((ToolchainModel tc) -> 
tc.getProvides().containsKey(CURRENT) ? -1 : +1);
+    }
+
+    Comparator<ToolchainModel> version() {
+        return comparing((ToolchainModel tc) -> 
tc.getProvides().getProperty(VERSION), (v1, v2) -> {
+                    String[] a = v1.split("\\.");
+                    String[] b = v2.split("\\.");
+                    int length = Math.min(a.length, b.length);
+                    for (int i = 0; i < length; i++) {
+                        String oa = a[i];
+                        String ob = b[i];
+                        if (!Objects.equals(oa, ob)) {
+                            // A null element is less than a non-null element
+                            if (oa == null || ob == null) {
+                                return oa == null ? -1 : 1;
+                            }
+                            int v = oa.compareTo(ob);
+                            if (v != 0) {
+                                return v;
+                            }
+                        }
+                    }
+                    return a.length - b.length;
+                })
+                .reversed();
+    }
+
+    private Set<Path> findJdks() {
+        if (foundJdks == null) {
+            synchronized (this) {
+                if (foundJdks == null) {
+                    foundJdks = doFindJdks();
+                }
+            }
+        }
+        return foundJdks;
+    }
+
+    private Set<Path> doFindJdks() {
+        List<Path> dirsToTest = new ArrayList<>();
+        // add current JDK
+        dirsToTest.add(Paths.get(System.getProperty(JAVA_HOME)));
+        // check environment variables for JAVA{xx}_HOME
+        System.getenv().entrySet().stream()
+                .filter(e -> e.getKey().startsWith("JAVA") && 
e.getKey().endsWith("_HOME"))
+                .map(e -> Paths.get(e.getValue()))
+                .forEach(dirsToTest::add);
+        final Path userHome = Paths.get(System.getProperty(USER_HOME));
+        List<Path> installedDirs = new ArrayList<>();
+        // jdk installed by third
+        installedDirs.add(userHome.resolve(".jdks"));
+        installedDirs.add(userHome.resolve(".m2").resolve("jdks"));
+        
installedDirs.add(userHome.resolve(".sdkman").resolve("candidates").resolve("java"));
+        installedDirs.add(userHome.resolve(".gradle").resolve("jdks"));
+        installedDirs.add(userHome.resolve(".jenv").resolve("versions"));
+        
installedDirs.add(userHome.resolve(".jbang").resolve("cache").resolve("jdks"));
+        installedDirs.add(userHome.resolve(".asdf").resolve("installs"));
+        installedDirs.add(userHome.resolve(".jabba").resolve("jdk"));
+        // os related directories
+        String osname = System.getProperty("os.name").toLowerCase(Locale.ROOT);
+        boolean macos = osname.startsWith("mac");
+        boolean win = osname.startsWith("win");
+        if (macos) {
+            installedDirs.add(Paths.get("/Library/Java/JavaVirtualMachines"));
+            
installedDirs.add(userHome.resolve("Library/Java/JavaVirtualMachines"));
+        } else if (win) {
+            installedDirs.add(Paths.get("C:\\Program Files\\Java\\"));
+            Path scoop = userHome.resolve("scoop").resolve("apps");
+            if (Files.isDirectory(scoop)) {
+                try (Stream<Path> stream = Files.list(scoop)) {
+                    stream.forEach(installedDirs::add);
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        } else {
+            installedDirs.add(Paths.get("/usr/jdk"));
+            installedDirs.add(Paths.get("/usr/java"));
+            installedDirs.add(Paths.get("/opt/java"));
+            installedDirs.add(Paths.get("/usr/lib/jvm"));
+        }
+        for (Path dest : installedDirs) {
+            if (Files.isDirectory(dest)) {
+                try (Stream<Path> stream = Files.list(dest)) {
+                    stream.forEach(dir -> {
+                        dirsToTest.add(dir);
+                        if (macos) {
+                            
dirsToTest.add(dir.resolve("Contents").resolve("Home"));
+                        }
+                    });
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+        // only keep directories that have a javac file
+        return dirsToTest.stream()
+                .filter(ToolchainDiscoverer::hasJavaC)
+                .map(ToolchainDiscoverer::getCanonicalPath)
+                .collect(Collectors.toSet());
+    }
+
+    private static boolean hasJavaC(Path subdir) {
+        return Files.exists(subdir.resolve(Paths.get("bin", "javac")))
+                || Files.exists(subdir.resolve(Paths.get("bin", "javac.exe")));
+    }
+}
diff --git a/src/site/apt/index.apt.vm b/src/site/apt/index.apt.vm
index b5bf949..56250dc 100644
--- a/src/site/apt/index.apt.vm
+++ b/src/site/apt/index.apt.vm
@@ -30,11 +30,28 @@ ${project.name}
 
   Similarly to the maven-enforcer-plugin, it allows you to control 
environmental constraints in the build.
 
+* Discovery mechanism
+
+  Since version 3.2.0, a new toolchains mechanism is provided.  This relies on 
an automatic discovery mechanism based
+  on an internal heuristic which tries to detect JDK from known locations.  
This mechanism is to be used with the
+  <<<select-jdk-toolchain>>> goal, read about the 
{{{./toolchains/discovery.html}discovery mechanism}} for more
+  informations.
+
 * Goals Overview
 
-  The Toolchains plugin has one goal:
+  Since version 3.2.0, a new toolchains mechanism is provided.  This relies on 
an automatic discovery mechanism based
+  on an internal heuristic which tries to detect JDK from known locations.  
This mechanism is to be used with the goal:
+
+    * {{{./toolchain-mojo.html}toolchains:select-jdk-toolchain}} discover and 
selects a matching toolchain.
+
+  Two helper goals are also provided:
+
+    * {{{./toolchain-mojo.html}toolchains:display-discovered-jdk-toolchains}} 
displays discovered toolchains to the console.
+    * {{{./toolchain-mojo.html}toolchains:generate-jdk-toolchains-xml}} can be 
used to write a <<<toolchains.xml>>> containing discovered JDKs.
+
+  The previous <<<toolchain>>> goal is still available:
 
-  * {{{./toolchain-mojo.html}toolchains:toolchain}} selects a toolchain based 
on configured build requirements and stores it in build context for later 
retrieval by other plugins.
+    * {{{./toolchain-mojo.html}toolchains:toolchain}} selects a toolchain 
based on configured build requirements and stores it in build context for later 
retrieval by other plugins.
 
 * Usage
 
diff --git a/src/site/apt/toolchains/discovery.apt.vm 
b/src/site/apt/toolchains/discovery.apt.vm
new file mode 100644
index 0000000..e528bc6
--- /dev/null
+++ b/src/site/apt/toolchains/discovery.apt.vm
@@ -0,0 +1,147 @@
+~~ Licensed to the Apache Software Foundation (ASF) under one
+~~ or more contributor license agreements.  See the NOTICE file
+~~ distributed with this work for additional information
+~~ regarding copyright ownership.  The ASF licenses this file
+~~ to you under the Apache License, Version 2.0 (the
+~~ "License"); you may not use this file except in compliance
+~~ with the License.  You may obtain a copy of the License at
+~~
+~~ http://www.apache.org/licenses/LICENSE-2.0
+~~
+~~ Unless required by applicable law or agreed to in writing,
+~~ software distributed under the License is distributed on an
+~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+~~ KIND, either express or implied.  See the License for the
+~~ specific language governing permissions and limitations
+~~ under the License.
+
+  ------
+  Discovery mechanism
+  ------
+  Guillaume Nodet
+  ------
+  2024-02-28
+  ------
+
+JDK Toolchain discovery mechanism
+
+  Since version 3.2.0, the plugin provides a heuristic to discover installed 
JDK toolchains, by looking
+  at known installation directories and at environment variables.
+
+  The list of discovered toolchains can be easily displayed using the command
+  <<<mvn 
org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>.
+  This will print something like:
+
++---+
+[INFO] Discovered 10 JDK toolchains:
+[INFO]   - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce
+[INFO]     provides:
+[INFO]       version: 21.0.2
+[INFO]       runtime.name: OpenJDK Runtime Environment
+[INFO]       runtime.version: 21.0.2+13-jvmci-23.1-b30
+[INFO]       vendor: GraalVM Community
+[INFO]       vendor.version: GraalVM CE 21.0.2+13.1
+[INFO]       current: true
+[INFO]       lts: true
+[INFO]       env: JAVA_HOME,JAVA21_HOME
+...
++---+
+
+  If you have installed JDKs using known installers and they are not found by 
the plugin,
+  feel free to {{{../issue-management.html}raise an issue}}.
+
+  The discovery mechanism provides information for each discovered JDK:
+
+   * <<<version>>>: the JDK version
+
+   * <<<runtime.name>>>: the name of the JDK runtime
+
+   * <<<runtime.version>>>: the version of the JDK runtime
+
+   * <<<vendor>>>: the vendor name
+
+   * <<<vendor.version>>>: the vendor version
+
+   * <<<current>>>: set to <<<true>>> if this is the running JDK
+
+   * <<<lts>>>: set to <<<true>>> if JDK version is a long-term supported 
version
+
+   * <<<env>>>: set to the comma separated list of <<<JAVA\{xyz\}_HOME>>>> 
matching environment variables
+
+
+  The <<<select-jdk-toolchain>>> goal finds a matching JDK.
+  The config below allows using the current JDK, or any other discovered JDK 
>= 17.
+  The current JDK can be kept for speed, but JDK 17 or higher will be used if 
the current JDK is older than 17.
+
++---+
+<properties>
+  <toolchain.jdk.version>[17,)</toolchain.jdk.version>
+<properties>
+
+<plugin>
+  <groupId>org.apache.maven.plugins</groupId>
+  <artifactId>maven-toolchains-plugin</artifactId>
+  <version>${project.version}</version>
+  <executions>
+    <execution>
+      <goals>
+        <goal>select-jdk-toolchain</goal>
+      </goals>
+    </execution>
+  </executions>
+</plugin>
++---+
+
+  If you use environment variables to configure your JDKs, you can use the 
following configuration to select
+  the toolchain which is configured using the <<<JAVA17_HOME>>> environment 
variable.
+
++---+
+<properties>
+  <toolchain.jdk.version>JAVA17_HOME</toolchain.jdk.version>
+<properties>
++---+
+
+* Selection mechanism
+
+  Several properties can express requirements to match against discovered JDK 
toolchains:
+
+   * <<<version>>> / <<<toolchain.jdk.version>>>: a version range such as 
<<<[17,18)>>> to match against the JDK version
+
+   * <<<runtimeName>>> / <<<toolchain.jdk.runtime.name>>>
+
+   * <<<runtimeVersion>>> / <<<toolchain.jdk.runtime.version>>>
+
+   * <<<vendor>>> / <<<toolchain.jdk.vendor>>>
+
+   * <<<env>>> / <<<toolchain.jdk.env>>>: the name of an environment variable 
that the JDK toolchain must match
+
+  The <<<useJdk>>> can be used to define whether the current JDK can be used 
if it matches the requirements.
+
+* Sorting
+
+  Multiple discovered JDK toolchains may satisfy the requirements.  In such a 
case, you can express
+  preferences for sorting the toolchains.  This can be done using the 
<<<comparator>>> configuration which is a
+  comma separated list of criteria amongst the following:
+
+   * <<<lts>>>: prefer LTS toolchains
+
+   * <<<current>>>: prefer the current JDK
+
+   * <<<env>>>: prefer toolchains discovered from environment variables
+
+   * <<<version>>>: prefer higher JDK versions
+
+   * <<<vendor>>>: sort alphabetically by vendor name
+
+  The default value is <<<lts,current,env,version,vendor>>>.
+
+* Toolchains XML file
+
+  The generation of the <<<toolchains.xml>>> file is not necessary to use 
discovered toolchains.
+  The <<<select-jdk-toolchain>>> will select a toolchain amongst explicitly 
configured toolchains and discovered
+  toolchains. Discovered toolchains are cached in 
<<<~/.m2/discovered-toolchains-cache.xml>>> file
+  by default, to speed up builds.
+
+  If you prefer, you can use the <<<generate-jdk-toolchains-xml>>> to generate 
a toolchain XML.  This can be used in
+  conjunction with the <<<discoverToolchains=false>>> configuration to disable 
discovery and only use explicitly
+  configured toolchains.
diff --git a/src/site/apt/toolchains/jdk.apt.vm 
b/src/site/apt/toolchains/jdk.apt.vm
index 85f3804..9b5a824 100644
--- a/src/site/apt/toolchains/jdk.apt.vm
+++ b/src/site/apt/toolchains/jdk.apt.vm
@@ -25,6 +25,7 @@
 
 JDK Toolchain
 
+Note that this page refers to hand-written JDK toolchains.  For a simpler 
setup, look at the {{{./discovery.html}discovery mechanism}}.
 
 * Toolchain Description
 
diff --git a/src/site/site.xml b/src/site/site.xml
index 5be1462..2989a7b 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -26,10 +26,14 @@ under the License.
     <menu name="Overview">
       <item name="Introduction" href="index.html"/>
       <item name="Goals" href="plugin-info.html">
+        <item name="toolchains:select-jdk-toolchain" 
href="select-jdk-toolchain-mojo.html"/>
         <item name="toolchains:toolchain" href="toolchain-mojo.html"/>
+        <item name="toolchains:display-discovered-jdk-toolchains-xml" 
href="display-discovered-jdk-toolchains-xml-mojo.html"/>
+        <item name="toolchains:generate-jdk-toolchains-xml" 
href="generate-jdk-toolchains-xml-mojo.html"/>
         <item name="toolchains:help" href="help-mojo.html"/>
       </item>
       <item name="Usage" href="usage.html">
+        <item name="Discovery mechanism" href="toolchains/discovery.html"/>
         <item name="Standard Toolchains" href="toolchains/index.html"/>
         <item name="JDK Standard Toolchain" href="toolchains/jdk.html"/>
         <item name="Custom Toolchains" href="toolchains/custom.html"/>
diff --git 
a/src/test/java/org/apache/maven/plugins/toolchain/jdk/ToolchainDiscovererTest.java
 
b/src/test/java/org/apache/maven/plugins/toolchain/jdk/ToolchainDiscovererTest.java
new file mode 100644
index 0000000..11657c0
--- /dev/null
+++ 
b/src/test/java/org/apache/maven/plugins/toolchain/jdk/ToolchainDiscovererTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.toolchain.jdk;
+
+import org.apache.maven.toolchain.model.PersistedToolchains;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnJre;
+import org.junit.jupiter.api.condition.JRE;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static 
org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.CURRENT;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ToolchainDiscovererTest {
+
+    final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Test
+    @DisabledOnJre(JRE.JAVA_8) // java 8 often has jdk != jre
+    void testDiscovery() {
+        ToolchainDiscoverer discoverer = new ToolchainDiscoverer();
+        PersistedToolchains persistedToolchains = 
discoverer.discoverToolchains();
+        assertNotNull(persistedToolchains);
+
+        persistedToolchains.getToolchains().forEach(model -> {
+            logger.info("  - "
+                    + ((Xpp3Dom) 
model.getConfiguration()).getChild("jdkHome").getValue());
+            logger.info("    provides:");
+            model.getProvides().forEach((k, v) -> logger.info("      " + k + 
": " + v));
+        });
+
+        assertTrue(persistedToolchains.getToolchains().stream()
+                .anyMatch(tc -> tc.getProvides().containsKey(CURRENT)));
+    }
+}

Reply via email to