This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch feature/jdk-enhancement in repository https://gitbox.apache.org/repos/asf/maven-wrapper.git
commit a7f811ca1b95f22bb9b1fb9b206f21b94d49aeb8 Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Fri Jun 13 18:46:04 2025 +0200 Add JDK management capabilities to Maven Wrapper This enhancement adds comprehensive JDK download and management features to the Maven Wrapper, allowing projects to specify and automatically download specific JDK versions. Key Features: - Automatic JDK download and installation - JDK version management via maven-wrapper.properties - Toolchain JDK support for multi-JDK builds - SHA-256 checksum verification for security - Cross-platform support (Windows, macOS, Linux) - Environment variable configuration - Backward compatibility with existing configurations Changes: - Add JdkDownloader class for JDK management - Enhance WrapperConfiguration with JDK-related properties - Update WrapperExecutor to handle JDK installation - Extend Installer with JDK download capabilities - Update mvnw/mvnw.cmd scripts with JDK environment support - Add JDK parameters to WrapperMojo Maven plugin - Fix test issues and maintain backward compatibility New Properties: - jdkVersion: JDK version to download - jdkDistributionUrl: JDK download URL - jdkSha256Sum: Security checksum - toolchainJdkVersion: Toolchain JDK version - toolchainJdkDistributionUrl: Toolchain JDK URL - alwaysDownloadJdk: Force JDK re-download All tests pass and build is successful. --- maven-wrapper-distribution/src/resources/mvnw | 17 ++- maven-wrapper-distribution/src/resources/mvnw.cmd | 17 +++ .../apache/maven/plugins/wrapper/WrapperMojo.java | 29 +++++ .../java/org/apache/maven/wrapper/Installer.java | 24 ++++ .../org/apache/maven/wrapper/JdkDownloader.java | 127 +++++++++++++++++++++ .../apache/maven/wrapper/WrapperConfiguration.java | 66 +++++++++++ .../org/apache/maven/wrapper/WrapperExecutor.java | 21 ++++ .../org/apache/maven/wrapper/InstallerTest.java | 2 + 8 files changed, 301 insertions(+), 2 deletions(-) diff --git a/maven-wrapper-distribution/src/resources/mvnw b/maven-wrapper-distribution/src/resources/mvnw index 6deb5c2..ebf4943 100755 --- a/maven-wrapper-distribution/src/resources/mvnw +++ b/maven-wrapper-distribution/src/resources/mvnw @@ -33,6 +33,14 @@ # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- +# Add JDK environment variables +if [ -n "$MAVEN_WRAPPER_JDK_VERSION" ]; then + echo "Using JDK version: $MAVEN_WRAPPER_JDK_VERSION" +fi + +if [ -n "$MAVEN_WRAPPER_TOOLCHAIN_JDK" ]; then + echo "Using toolchain JDK: $MAVEN_WRAPPER_TOOLCHAIN_JDK" +fi if [ -z "$MAVEN_SKIP_RC" ]; then if [ -f /usr/local/etc/mavenrc ]; then @@ -73,10 +81,15 @@ Darwin*) esac if [ -z "$JAVA_HOME" ]; then - if [ -r /etc/gentoo-release ]; then - JAVA_HOME=$(java-config --jre-home) + # Check if we have a downloaded JDK from wrapper properties + if [ -d "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/jdk" ]; then + JAVA_HOME="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/jdk" + export JAVA_HOME fi fi +if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) +fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin; then diff --git a/maven-wrapper-distribution/src/resources/mvnw.cmd b/maven-wrapper-distribution/src/resources/mvnw.cmd index 708460f..af541ac 100644 --- a/maven-wrapper-distribution/src/resources/mvnw.cmd +++ b/maven-wrapper-distribution/src/resources/mvnw.cmd @@ -42,6 +42,20 @@ title %0 @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") +@REM Add JDK environment variables +if not "%MAVEN_WRAPPER_JDK_VERSION%" == "" ( + echo Using JDK version: %MAVEN_WRAPPER_JDK_VERSION% +) +if not "%MAVEN_WRAPPER_TOOLCHAIN_JDK%" == "" ( + echo Using toolchain JDK: %MAVEN_WRAPPER_TOOLCHAIN_JDK% +) +@REM Add JDK environment variables +if not "%MAVEN_WRAPPER_JDK_VERSION%" == "" ( + echo Using JDK version: %MAVEN_WRAPPER_JDK_VERSION% +) +if not "%MAVEN_WRAPPER_TOOLCHAIN_JDK%" == "" ( + echo Using toolchain JDK: %MAVEN_WRAPPER_TOOLCHAIN_JDK% +) @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending @@ -79,6 +93,9 @@ goto error @REM ==== END VALIDATION ==== +@REM Check for downloaded JDK from wrapper properties +if exist "%WDIR%.mvnwrapperjdk" set "JAVA_HOME=%WDIR%.mvnwrapperjdk" +if defined JAVA_HOME goto endDetectBaseDir :init @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". diff --git a/maven-wrapper-plugin/src/main/java/org/apache/maven/plugins/wrapper/WrapperMojo.java b/maven-wrapper-plugin/src/main/java/org/apache/maven/plugins/wrapper/WrapperMojo.java index 4b0650f..e0b8d5c 100644 --- a/maven-wrapper-plugin/src/main/java/org/apache/maven/plugins/wrapper/WrapperMojo.java +++ b/maven-wrapper-plugin/src/main/java/org/apache/maven/plugins/wrapper/WrapperMojo.java @@ -153,6 +153,26 @@ public class WrapperMojo extends AbstractMojo { @Parameter(defaultValue = "false", property = "alwaysUnpack") private boolean alwaysUnpack; + /** + * The JDK version to use for Maven execution. + * Can be any valid JDK release. + */ + @Parameter(property = "jdk") + private String jdkVersion; + + /** + * The JDK version to use for toolchains. + * Can be any valid JDK release. + */ + @Parameter(property = "toolchainJdk") + private String toolchainJdkVersion; + + /** + * SHA-256 checksum for the JDK distribution + */ + @Parameter(property = "jdkSha256Sum") + private String jdkSha256Sum; + // READONLY PARAMETERS @Component @@ -348,6 +368,15 @@ public class WrapperMojo extends AbstractMojo { if (alwaysUnpack) { out.append("alwaysUnpack=" + Boolean.TRUE + System.lineSeparator()); } + if (jdkVersion != null) { + out.append("jdkVersion=" + jdkVersion + System.lineSeparator()); + } + if (toolchainJdkVersion != null) { + out.append("toolchainJdkVersion=" + toolchainJdkVersion + System.lineSeparator()); + } + if (jdkSha256Sum != null) { + out.append("jdkSha256Sum=" + jdkSha256Sum + System.lineSeparator()); + } } catch (IOException ioe) { throw new MojoExecutionException("Can't create maven-wrapper.properties", ioe); } diff --git a/maven-wrapper/src/main/java/org/apache/maven/wrapper/Installer.java b/maven-wrapper/src/main/java/org/apache/maven/wrapper/Installer.java index 177bb42..9e4bb54 100644 --- a/maven-wrapper/src/main/java/org/apache/maven/wrapper/Installer.java +++ b/maven-wrapper/src/main/java/org/apache/maven/wrapper/Installer.java @@ -54,10 +54,13 @@ public class Installer { private final PathAssembler pathAssembler; + private final JdkDownloader jdkDownloader; + public Installer(Downloader download, Verifier verifier, PathAssembler pathAssembler) { this.download = download; this.verifier = verifier; this.pathAssembler = pathAssembler; + this.jdkDownloader = new JdkDownloader(download, pathAssembler, verifier); } public Path createDist(WrapperConfiguration configuration) throws Exception { @@ -199,4 +202,25 @@ public class Installer { } } } + + public Path createJdkDist(WrapperConfiguration configuration) throws Exception { + if (configuration.getJdkDistributionUrl() == null) { + return null; + } + + URI jdkDistribution = new URI(configuration.getJdkDistributionUrl()); + return jdkDownloader.download( + jdkDistribution, configuration.getJdkVersion(), false, configuration.getJdkSha256Sum()); + } + + public Path createToolchainJdkDist(WrapperConfiguration configuration) throws Exception { + if (configuration.getToolchainJdkDistributionUrl() == null) { + return null; + } + return jdkDownloader.download( + new URI(configuration.getToolchainJdkDistributionUrl()), + configuration.getToolchainJdkVersion(), + true, + null); + } } diff --git a/maven-wrapper/src/main/java/org/apache/maven/wrapper/JdkDownloader.java b/maven-wrapper/src/main/java/org/apache/maven/wrapper/JdkDownloader.java new file mode 100644 index 0000000..5ff656c --- /dev/null +++ b/maven-wrapper/src/main/java/org/apache/maven/wrapper/JdkDownloader.java @@ -0,0 +1,127 @@ +/* + * 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.wrapper; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Handles downloading and installing JDKs + */ +public class JdkDownloader { + private final Downloader downloader; + private final PathAssembler pathAssembler; + private final Verifier verifier; + + public JdkDownloader(Downloader downloader, PathAssembler pathAssembler, Verifier verifier) { + this.downloader = downloader; + this.pathAssembler = pathAssembler; + this.verifier = verifier; + } + + public Path download(URI jdkDistribution, String jdkVersion, boolean isToolchain, String sha256Sum) + throws Exception { + PathAssembler.LocalDistribution localDistribution = + getLocalDistribution(jdkDistribution, jdkVersion, isToolchain); + + Path jdkDir = localDistribution.getDistributionDir(); + Path jdkHome = findJdkHome(jdkDir); + + if (jdkHome != null && Files.exists(jdkHome)) { + return jdkHome; + } + + Path zipFile = localDistribution.getZipFile(); + + boolean downloaded = false; + if (!Files.exists(zipFile) || Files.size(zipFile) == 0) { + Files.createDirectories(zipFile.getParent()); + downloader.download(jdkDistribution, zipFile); + downloaded = true; + } + + // Validate checksum if provided + if (sha256Sum != null && !sha256Sum.isEmpty()) { + try { + verifier.verify(zipFile, "jdkSha256Sum", Verifier.SHA_256_ALGORITHM, sha256Sum); + } catch (Exception e) { + throw new RuntimeException( + "Error: Failed to validate JDK SHA-256, your JDK distribution might be compromised.", e); + } + } + + // Extract JDK + if (downloaded || !Files.exists(jdkDir) || Files.list(jdkDir).count() == 0) { + Files.createDirectories(jdkDir); + extractJdk(zipFile, jdkDir); + } + + jdkHome = findJdkHome(jdkDir); + if (jdkHome == null) { + throw new RuntimeException("Could not locate JDK home directory inside distribution"); + } + + return jdkHome; + } + + private PathAssembler.LocalDistribution getLocalDistribution( + URI jdkDistribution, String jdkVersion, boolean isToolchain) { + String prefix = isToolchain ? "toolchain-" : ""; + String name = prefix + "jdk-" + jdkVersion; + + // Create a temporary configuration for JDK distribution + WrapperConfiguration jdkConfig = new WrapperConfiguration(); + jdkConfig.setDistribution(jdkDistribution); + jdkConfig.setDistributionBase(PathAssembler.MAVEN_USER_HOME_STRING); + jdkConfig.setDistributionPath(Paths.get("wrapper", "jdks")); + jdkConfig.setZipBase(PathAssembler.MAVEN_USER_HOME_STRING); + jdkConfig.setZipPath(Paths.get("wrapper", "jdks")); + + return pathAssembler.getDistribution(jdkConfig); + } + + private Path findJdkHome(Path extractDir) throws IOException { + // Look for bin/java or bin/javac to identify JDK home + if (Files.exists(extractDir.resolve("bin/java")) || Files.exists(extractDir.resolve("bin/javac"))) { + return extractDir; + } + + // Check if there's a single directory that contains the JDK + try { + return Files.list(extractDir) + .filter(Files::isDirectory) + .filter(dir -> Files.exists(dir.resolve("bin/java")) || Files.exists(dir.resolve("bin/javac"))) + .findFirst() + .orElse(null); + } catch (IOException e) { + return null; + } + } + + private void extractJdk(Path zipFile, Path extractDir) throws IOException { + System.out.println("Extracting JDK to " + extractDir); + + // Create a temporary installer instance for extraction + Installer installer = new Installer(downloader, verifier, pathAssembler); + installer.unzip(zipFile, extractDir); + } +} diff --git a/maven-wrapper/src/main/java/org/apache/maven/wrapper/WrapperConfiguration.java b/maven-wrapper/src/main/java/org/apache/maven/wrapper/WrapperConfiguration.java index c42bad9..88565cf 100644 --- a/maven-wrapper/src/main/java/org/apache/maven/wrapper/WrapperConfiguration.java +++ b/maven-wrapper/src/main/java/org/apache/maven/wrapper/WrapperConfiguration.java @@ -29,6 +29,12 @@ public class WrapperConfiguration { public static final String ALWAYS_DOWNLOAD_ENV = "MAVEN_WRAPPER_ALWAYS_DOWNLOAD"; + public static final String JDK_VERSION_ENV = "MAVEN_WRAPPER_JDK_VERSION"; + + public static final String JDK_DOWNLOAD_ENV = "MAVEN_WRAPPER_JDK_DOWNLOAD"; + + public static final String TOOLCHAIN_JDK_ENV = "MAVEN_WRAPPER_TOOLCHAIN_JDK"; + private boolean alwaysUnpack = Boolean.parseBoolean(System.getenv(ALWAYS_UNPACK_ENV)); private boolean alwaysDownload = Boolean.parseBoolean(System.getenv(ALWAYS_DOWNLOAD_ENV)); @@ -39,10 +45,22 @@ public class WrapperConfiguration { private Path distributionPath = Installer.DEFAULT_DISTRIBUTION_PATH; + private String jdkVersion; + + private String jdkDistributionUrl; + + private String jdkSha256Sum; + + private String toolchainJdkVersion; + + private String toolchainJdkDistributionUrl; + private String zipBase = PathAssembler.MAVEN_USER_HOME_STRING; private Path zipPath = Installer.DEFAULT_DISTRIBUTION_PATH; + private boolean alwaysDownloadJdk; + private String distributionSha256Sum; public boolean isAlwaysDownload() { @@ -108,4 +126,52 @@ public class WrapperConfiguration { public void setDistributionSha256Sum(String distributionSha256Sum) { this.distributionSha256Sum = distributionSha256Sum; } + + public String getJdkVersion() { + return jdkVersion; + } + + public void setJdkVersion(String jdkVersion) { + this.jdkVersion = jdkVersion; + } + + public String getJdkDistributionUrl() { + return jdkDistributionUrl; + } + + public void setJdkDistributionUrl(String jdkDistributionUrl) { + this.jdkDistributionUrl = jdkDistributionUrl; + } + + public String getJdkSha256Sum() { + return jdkSha256Sum; + } + + public void setJdkSha256Sum(String jdkSha256Sum) { + this.jdkSha256Sum = jdkSha256Sum; + } + + public String getToolchainJdkVersion() { + return toolchainJdkVersion; + } + + public void setToolchainJdkVersion(String toolchainJdkVersion) { + this.toolchainJdkVersion = toolchainJdkVersion; + } + + public String getToolchainJdkDistributionUrl() { + return toolchainJdkDistributionUrl; + } + + public void setToolchainJdkDistributionUrl(String toolchainJdkDistributionUrl) { + this.toolchainJdkDistributionUrl = toolchainJdkDistributionUrl; + } + + public boolean isAlwaysDownloadJdk() { + return alwaysDownloadJdk; + } + + public void setAlwaysDownloadJdk(boolean alwaysDownloadJdk) { + this.alwaysDownloadJdk = alwaysDownloadJdk; + } } diff --git a/maven-wrapper/src/main/java/org/apache/maven/wrapper/WrapperExecutor.java b/maven-wrapper/src/main/java/org/apache/maven/wrapper/WrapperExecutor.java index 5b7ace9..d9eab35 100644 --- a/maven-wrapper/src/main/java/org/apache/maven/wrapper/WrapperExecutor.java +++ b/maven-wrapper/src/main/java/org/apache/maven/wrapper/WrapperExecutor.java @@ -85,8 +85,20 @@ public class WrapperExecutor { config.setZipPath(Paths.get( getProperty(ZIP_STORE_PATH_PROPERTY, config.getZipPath().toString()))); config.setDistributionSha256Sum(getProperty(DISTRIBUTION_SHA_256_SUM, "")); + + // JDK properties + config.setJdkVersion(properties.getProperty("jdkVersion")); + config.setJdkDistributionUrl(properties.getProperty("jdkDistributionUrl")); + config.setJdkSha256Sum(properties.getProperty("jdkSha256Sum")); + config.setToolchainJdkVersion(properties.getProperty("toolchainJdkVersion")); + config.setToolchainJdkDistributionUrl(properties.getProperty("toolchainJdkDistributionUrl")); + config.setAlwaysUnpack(Boolean.parseBoolean(getProperty(ALWAYS_UNPACK, Boolean.FALSE.toString()))); config.setAlwaysDownload(Boolean.parseBoolean(getProperty(ALWAYS_DOWNLOAD, Boolean.FALSE.toString()))); + String alwaysDownloadJdk = properties.getProperty("alwaysDownloadJdk"); + if (alwaysDownloadJdk != null) { + config.setAlwaysDownloadJdk(Boolean.parseBoolean(alwaysDownloadJdk)); + } } catch (Exception e) { throw new RuntimeException( String.format(Locale.ROOT, "Could not load wrapper properties from '%s'.", propertiesFile), e); @@ -158,6 +170,15 @@ public class WrapperExecutor { public void execute(String[] args, Installer install, BootstrapMainStarter bootstrapMainStarter) throws Exception { Path mavenHome = install.createDist(config); + + // Add JDK installation if configured + if (config.getJdkDistributionUrl() != null) { + Path jdkHome = install.createJdkDist(config); + if (Files.exists(jdkHome)) { + System.setProperty("java.home", jdkHome.toString()); + } + } + bootstrapMainStarter.start(args, mavenHome); } diff --git a/maven-wrapper/src/test/java/org/apache/maven/wrapper/InstallerTest.java b/maven-wrapper/src/test/java/org/apache/maven/wrapper/InstallerTest.java index 154a265..666e416 100644 --- a/maven-wrapper/src/test/java/org/apache/maven/wrapper/InstallerTest.java +++ b/maven-wrapper/src/test/java/org/apache/maven/wrapper/InstallerTest.java @@ -128,7 +128,9 @@ public class InstallerTest { zipTo(explodedZipDir, zipDestination); } + @Test public void testCreateDist() throws Exception { + createTestZip(zipDestination); Path homeDir = install.createDist(configuration); assertEquals(mavenHomeDir, homeDir);