maven-wrapper icon indicating copy to clipboard operation
maven-wrapper copied to clipboard

Add JDK management with Foojay Disco API integration

Open gnodet opened this issue 5 months ago โ€ข 44 comments

๐Ÿš€ JDK Management for Maven Wrapper

This PR adds comprehensive JDK management capabilities to Maven Wrapper using the Foojay Disco API, enabling automatic JDK download and installation for the only-script distribution type.

โœจ Key Features

๐ŸŽฏ Automatic JDK Management

  • Automatic JDK download and installation for only-script distribution
  • 34+ JDK distributions supported via Foojay Disco API
  • Version resolution: Major versions (e.g., 17) resolve to latest patch versions
  • Multi-platform support: Linux, macOS, Windows with architecture detection
  • Toolchain integration: Automatic toolchains.xml generation for multi-JDK builds
  • Dynamic LTS detection: Uses Disco API to identify LTS versions (no hardcoded heuristics)
  • Bypass option: MVNW_SKIP_JDK environment variable to use system JDK

๐Ÿ”ง Configuration Options

# Basic JDK configuration
jdkVersion=17
jdkDistribution=temurin

# Toolchain JDK for multi-version builds
toolchainJdkVersion=11
toolchainJdkDistribution=corretto

# Update policies
jdkUpdatePolicy=daily  # never, daily, weekly, monthly, always, interval:X

# Direct URLs (optional)
jdkDistributionUrl=https://example.com/jdk.tar.gz
jdkSha256Sum=abc123...

๐Ÿ†” Environment Variables

# Skip JDK management and use system JDK
export MVNW_SKIP_JDK=true

# Enable verbose output
export MVNW_VERBOSE=true

# Configure JDK settings
export MAVEN_WRAPPER_JDK_VERSION=17
export MAVEN_WRAPPER_JDK_DISTRIBUTION=temurin

๐Ÿ“ฆ Supported Distributions

Popular Distributions:

  • temurin - Eclipse Adoptium (default)
  • corretto - Amazon Corretto
  • zulu - Azul Zulu
  • liberica - BellSoft Liberica
  • oracle_open_jdk - Oracle OpenJDK
  • microsoft - Microsoft OpenJDK
  • semeru - IBM Semeru

Specialized Distributions:

  • graalvm_ce11, graalvm_ce17 - GraalVM Community Edition
  • sap_machine - SAP Machine
  • dragonwell - Alibaba Dragonwell
  • jetbrains - JetBrains Runtime
  • bisheng - Huawei BiSheng
  • kona - Tencent Kona
  • mandrel - Red Hat Mandrel

Complete list: Foojay Disco API

๐Ÿ› ๏ธ Usage Examples

Maven Plugin

# Generate wrapper with JDK management
mvn wrapper:wrapper -Dtype=only-script -Djdk=17 -DjdkDistribution=temurin

# With toolchain support
mvn wrapper:wrapper -Dtype=only-script -Djdk=21 -DjdkDistribution=corretto -DtoolchainJdk=17 -DtoolchainJdkDistribution=temurin

Properties Configuration

# Production: Pin exact version
jdkVersion=17.0.14
jdkDistribution=temurin
jdkUpdatePolicy=never

# Development: Auto-update to latest patches
jdkVersion=17
jdkDistribution=temurin
jdkUpdatePolicy=daily

Bypassing JDK Management

# Use system JDK instead of wrapper-managed JDK
export MVNW_SKIP_JDK=true
./mvnw clean compile

# Windows
set MVNW_SKIP_JDK=true
mvnw.cmd clean compile

# Useful for CI matrix testing
MVNW_SKIP_JDK=true ./mvnw test

Multi-JDK Toolchain Setup

# Main JDK for build
jdkVersion=21
jdkDistribution=temurin

# Toolchain JDK for compilation
toolchainJdkVersion=17
toolchainJdkDistribution=corretto

๐Ÿ—๏ธ Implementation Details

Architecture

  • Shell-based implementation for only-script distribution type
  • No Java dependency for JDK installation (avoids chicken-and-egg problem)
  • Foojay Disco API integration for JDK resolution and download
  • Cross-platform support with native shell scripts (Unix) and PowerShell (Windows)
  • API URL constants for maintainability and easy version updates

Dynamic LTS Detection

  • Real-time LTS detection via Disco API /major_versions endpoint
  • Intelligent caching to avoid repeated API calls
  • Robust fallback to hardcoded LTS list if API unavailable
  • Accurate warnings showing current LTS versions (6, 7, 8, 11, 17, 21)
  • Future-proof - automatically includes new LTS versions when released

Distribution Validation

  • Comprehensive validation against known Disco API distributions
  • Helpful error messages showing all available distributions
  • Typo prevention with clear distribution suggestions

Bypass Mechanisms

  • MVNW_SKIP_JDK: Skip JDK management entirely, use system JDK
  • Direct URLs: Override version/distribution resolution with exact URLs
  • Environment variables: Configure JDK settings without modifying properties

Caching & Performance

  • Intelligent caching with configurable update policies
  • SHA-256 verification for security
  • Efficient downloads with resume support
  • Version resolution caching to minimize API calls

Security Features

  • HTTPS downloads by default
  • SHA-256 checksum verification for all downloads
  • Automatic checksum resolution from Disco API
  • Manual checksum override for direct URLs

๐ŸŽฏ Benefits

For Developers

  • Zero JDK setup - automatic download and installation
  • Consistent environments across team members
  • Easy distribution switching for testing
  • Toolchain support for multi-JDK projects
  • Accurate LTS guidance with real-time data
  • Flexible bypass options for special cases

For CI/CD

  • Reproducible builds with exact version pinning
  • No pre-installed JDK requirement in build environments
  • Flexible update policies for different environments
  • Multi-platform consistency
  • Matrix testing support with MVNW_SKIP_JDK

For Projects

  • Simplified onboarding - no JDK installation instructions needed
  • Version consistency across all environments
  • Easy JDK upgrades via configuration changes
  • Professional distribution support (34+ options)
  • Troubleshooting options with bypass mechanisms

๐Ÿ”„ Migration Path

Existing projects can easily adopt JDK management:

  1. Switch to only-script distribution type
  2. Add JDK configuration to maven-wrapper.properties
  3. Remove manual JDK setup from documentation
  4. Enjoy automatic JDK management
# Simple migration command
mvn wrapper:wrapper -Dtype=only-script -Djdk=17 -DjdkDistribution=temurin

๐Ÿงช Testing

  • โœ… Cross-platform testing (Linux, macOS, Windows)
  • โœ… Multiple JDK distributions verified
  • โœ… Version resolution tested (major โ†’ patch versions)
  • โœ… Toolchain integration validated
  • โœ… Error handling and validation tested
  • โœ… Caching and update policies verified
  • โœ… Dynamic LTS detection confirmed with API
  • โœ… MVNW_SKIP_JDK bypass functionality tested

๐Ÿ“š Documentation

Comprehensive documentation added:

  • JDK_MANAGEMENT.md - Complete feature guide
  • Configuration examples for all use cases
  • Environment variable documentation
  • Troubleshooting guide for common issues
  • Migration instructions for existing projects
  • MVNW_SKIP_JDK usage examples

๐ŸŽ‰ Summary

This PR transforms Maven Wrapper into a complete JDK management solution that:

  • Eliminates JDK setup friction for developers
  • Ensures consistent environments across teams
  • Supports 34+ professional JDK distributions
  • Provides flexible configuration options for all use cases
  • Maintains security with checksum verification
  • Offers excellent performance with intelligent caching
  • Uses dynamic LTS detection with real-time API data
  • Requires zero maintenance for future JDK versions
  • Includes bypass mechanisms for special cases and troubleshooting

The implementation is production-ready, well-tested, and thoroughly documented, making it easy for projects to adopt automatic JDK management while maintaining flexibility for edge cases.

gnodet avatar Jul 16 '25 20:07 gnodet

Whoa .. thats amazing!

mosabua avatar Jul 16 '25 20:07 mosabua

Is my understanding correct that the mvnw script now downloads and installs a JDK distrubution instead of using the system JDK?

norrisjeremy avatar Jul 16 '25 22:07 norrisjeremy

Is my understanding correct that the mvnw script now downloads and installs a JDK distrubution instead of using the system JDK?

That's not completely exact. The JDK download is an opt-in if you add the required properties. The default behaviour is unchanged. There's also a MVNW_SKIP_JDK env var to skip using the JDK indicated by those properties (can be useful when running in a JDK matrix in a CI job).

gnodet avatar Jul 17 '25 06:07 gnodet

Is my understanding correct that the mvnw script now downloads and installs a JDK distrubution instead of using the system JDK?

That's not completely exact. The JDK download is an opt-in if you add the required properties. The default behaviour is unchanged. There's also a MVNW_SKIP_JDK env var to skip using the JDK indicated by those properties (can be useful when running in a JDK matrix in a CI job).

Ok, thanks! I was concerned that the default behavior was changing and users would have to explicitly opt-out to prevent it.

norrisjeremy avatar Jul 17 '25 11:07 norrisjeremy

Currently broken by https://github.com/foojayio/discoapi/issues/124

gnodet avatar Jul 22 '25 11:07 gnodet

To confirm my understanding: if we add the new JDK properties into .mvn/wrapper/maven-wrapper.properties, but then set the MVNW_SKIP_JDK=true env variable, mvnw will use the system JDK as always and not attempt to download a JDK?

norrisjeremy avatar Jul 23 '25 20:07 norrisjeremy

To confirm my understanding: if we add the new JDK properties into .mvn/wrapper/maven-wrapper.properties, but then set the MVNW_SKIP_JDK=true env variable, mvnw will use the system JDK as always and not attempt to download a JDK?

Correct

gnodet avatar Jul 23 '25 21:07 gnodet

To confirm my understanding: if we add the new JDK properties into .mvn/wrapper/maven-wrapper.properties, but then set the MVNW_SKIP_JDK=true env variable, mvnw will use the system JDK as always and not attempt to download a JDK?

Correct

Perfect, thanks! This sounds like a great feature which we can use with our dev team to allow easy bootstrapping of the preferred JDK distribution on their workstations, whilst continuing to have our Github Actions CI pipelines use the system provided JDK.

norrisjeremy avatar Jul 23 '25 21:07 norrisjeremy

Exciting feature!

@gnodet is it possible to support the .java-version file for defining the default JDK version of a project? It is normally generated by jenv to lock a project local JDK.

gzm55 avatar Jul 24 '25 00:07 gzm55

# Useful for CI matrix testing MVNW_SKIP_JDK=true ./mvnw test

@gnodet how about to support selecting dynamicly JDK version via an environment, then the mvnw can pin almost the jdk matrix by project.

gzm55 avatar Jul 24 '25 00:07 gzm55

# Useful for CI matrix testing MVNW_SKIP_JDK=true ./mvnw test

@gnodet how about to support selecting dynamicly JDK version via an environment, then the mvnw can pin almost the jdk matrix by project.

I've added env vars to override the JDK properties.

gnodet avatar Jul 24 '25 06:07 gnodet

# Useful for CI matrix testing MVNW_SKIP_JDK=true ./mvnw test

@gnodet how about to support selecting dynamicly JDK version via an environment, then the mvnw can pin almost the jdk matrix by project.

I've added env vars to override the JDK properties.

should we make all the env vars all has the same prefix MVNW_

gzm55 avatar Jul 24 '25 07:07 gzm55

I'm not on the committers list, but this looks like fantastic new feature! :)

carlspring avatar Jul 24 '25 09:07 carlspring

# Useful for CI matrix testing MVNW_SKIP_JDK=true ./mvnw test

@gnodet how about to support selecting dynamicly JDK version via an environment, then the mvnw can pin almost the jdk matrix by project.

I've added env vars to override the JDK properties.

should we make all the env vars all has the same prefix MVNW_

They do AFAIK, did I miss something ?

gnodet avatar Jul 24 '25 09:07 gnodet

# Useful for CI matrix testing MVNW_SKIP_JDK=true ./mvnw test

@gnodet how about to support selecting dynamicly JDK version via an environment, then the mvnw can pin almost the jdk matrix by project.

I've added env vars to override the JDK properties.

should we make all the env vars all has the same prefix MVNW_

They do AFAIK, did I miss something ?

maven-wrapper-plugin/src/it/projects/jdk_environment_variables/pom.xml left some MAVEN_WRAPPER_

gzm55 avatar Jul 24 '25 09:07 gzm55

It's a great feature. Maybe we shouldn't download some binaries per default but ask the user before downloading the idk?

olamy avatar Jul 25 '25 04:07 olamy

It's a great feature. Maybe we shouldn't download some binaries per default but ask the user before downloading the idk?

Not sure to understand. The default behaviour is unchanged, in order to have the JDK downloaded, the user needs to opt-in and add the needed properties, which will trigger the JDK download, same as the wrapper downloads the Maven distribution. This can be opt-out using env-var too. You would like an additional confirmation before actually downloading the JDK or the maven distribution ?

gnodet avatar Jul 25 '25 04:07 gnodet

It's a great feature. Maybe we shouldn't download some binaries per default but ask the user before downloading the idk?

Not sure to understand. The default behaviour is unchanged, in order to have the JDK downloaded, the user needs to opt-in and add the needed properties, which will trigger the JDK download, same as the wrapper downloads the Maven distribution. This can be opt-out using env-var too. You would like an additional confirmation before actually downloading the JDK or the maven distribution ?

I don't mind for Maven because it's Maven :) but for jdk is a different (we do not maintain and we don't even know what is the license) maybe it would be better to have additional confirmation (can be disable eventually by the generator of the script)

I just tried this:

mvn org.apache.maven.plugins:maven-wrapper-plugin:3.3.3-SNAPSHOT:wrapper -Dtype=only-script -Djdk=17 -DjdkDistribution=temurin 

Now if I push the generated script any user of the opensource project where I did that will have automatic download of a jdk binaries.

olamy avatar Jul 25 '25 04:07 olamy

Great feature! From a user perspective (especially if I wear the Enterprise and not the Open Source Developer one) I have few things:

  • Do you plan to release this feature a new major version of Maven Wrapper? I consider this a major change (although not necessarily breaking) since the default behaviour changes and the user has to opt-out using MVNW_SKIP_JDK. That is, a simple update of Maven Wrapper should need cause me to suddently have a bunch of JDK installed without my knowledge (especially, since we use SDKMan already).
  • Is there a way that this could be integrated with SDKMan for users that want that?
  • Have you considered security concerns with the distribution url? A person might check the source code, the POM etc for security stuff but fail to understand the implication of a distibution url that leads to a malicious JDK? I reckon if you use jdkDistribution then it uses the Foojay Disco API which seems to be some sort of assurance of the integrity of that JDK.

jimisola avatar Jul 25 '25 09:07 jimisola

  • Have you considered security concerns with the distribution url? A person might check the source code, the POM etc for security stuff but fail to understand the implication of a distibution url that leads to a malicious JDK? I reckon if you use jdkDistribution then it uses the Foojay Disco API which seems to be some sort of assurance of the integrity of that JDK.

Isn't the fact that you can pin the SHA-256 hash via the jdkSha256Sum property cover the security perspectives?

norrisjeremy avatar Jul 25 '25 09:07 norrisjeremy

  • Have you considered security concerns with the distribution url? A person might check the source code, the POM etc for security stuff but fail to understand the implication of a distibution url that leads to a malicious JDK? I reckon if you use jdkDistribution then it uses the Foojay Disco API which seems to be some sort of assurance of the integrity of that JDK.

Isn't the fact that you can pin the SHA-256 hash via the jdkSha256Sum property cover the security perspectives?

No. That only verifies that the file specified in the properties file is the file that you actually downloaded. It does not provide any "security guarantees" of that JDK.

E.g.

# Direct URLs (optional)
jdkDistributionUrl=https://hackers.com/jdk_that_has_malicous_code.tar.gz
jdkSha256Sum=<a valid sha256 sum for that tarball>.

This can of course be said for regular dependencies as well. The difference here is that users are used to dependencies they're not used to a random JDK being pulled in without their knowledge.

And there tends to be infrastructure (at least from an Enterprise perspective) to scan dependencies for CVEs etc which jdkDistributionUrl would completely circumvent.

Just to be clear, I'm not against the jdkDistributionUrl - it might be useful. But, when it is used the user should be made aware (read: warned about security risks) and then actively have to approve it (which can be explicitly pre-approved using a command line option or similar).

jimisola avatar Jul 25 '25 09:07 jimisola

No. That only verifies that the file specified in the properties file is the file that you actually downloaded. It does not provide any "security guarantees" of that JDK.

E.g.

# Direct URLs (optional)
jdkDistributionUrl=https://hackers.com/jdk_that_has_malicous_code.tar.gz
jdkSha256Sum=<a valid sha256 sum for that tarball>.

This can of course be said for regular dependencies as well. The difference here is that users are used to dependencies they're not used to a random JDK being pulled in without their knowledge.

And there tends to be infrastructure (at least from an Enterprise perspective) to scan dependencies for CVEs etc which jdkDistributionUrl would completely circumvent.

I sort of surprised that an enterprise that is that particular about security would use maven-wrapper in the first place, but to each their own. ๐Ÿคทโ€โ™‚๏ธ

norrisjeremy avatar Jul 25 '25 09:07 norrisjeremy

Great feature! From a user perspective (especially if I wear the Enterprise and not the Open Source Developer one) I have few things:

  • Do you plan to release this feature a new major version of Maven Wrapper? I consider this a major change (although not necessarily breaking) since the default behaviour changes and the user has to opt-out using MVNW_SKIP_JDK. That is, a simple update of Maven Wrapper should need cause me to suddently have a bunch of JDK installed without my knowledge (especially, since we use SDKMan already).

Not really because the default behaviour DOES NOT change. You have to first opt-in and specify the JDK version. Once you have opted-in, you can then opt-out with MVNW_SKIP_JDK.

  • Is there a way that this could be integrated with SDKMan for users that want that?

Not sure. That was really my first attempt. Unfortunately, sdkman does not keep track of old releases, which is a problem from a reproducibility pov.

  • Have you considered security concerns with the distribution url? A person might check the source code, the POM etc for security stuff but fail to understand the implication of a distibution url that leads to a malicious JDK? I reckon if you use jdkDistribution then it uses the Foojay Disco API which seems to be some sort of assurance of the integrity of that JDK.

I agree with Jeremy here. A simple to secure things is to use an exact url + sha256.

If you want to keep some versatility, another way would be to be able to provide an alternative REST endpoint that could be implemented within an entreprise. At runtime, only https://api.foojay.io/disco/v3.0/packages is actually used. So an env var could specify a REST endpoint different than the default https://api.foojay.io/disco/v3.0.

gnodet avatar Jul 25 '25 09:07 gnodet

I sort of surprised that an enterprise that is that particular about security would use maven-wrapper in the first place, but to each their own. ๐Ÿคทโ€โ™‚๏ธ

It's not a matter of Enterprise or not. When I develop on my spare time I don't necessarily want to have a bunch of JDKs downloaded to my computer without my knowledge.

jimisola avatar Jul 25 '25 09:07 jimisola

  • Do you plan to release this feature a new major version of Maven Wrapper? I consider this a major change (although not necessarily breaking) since the default behaviour changes and the user has to opt-out using MVNW_SKIP_JDK. That is, a simple update of Maven Wrapper should need cause me to suddently have a bunch of JDK installed without my knowledge (especially, since we use SDKMan already).

Not really because the default behaviour DOES NOT change. You have to first opt-in and specify the JDK version. Once you have opted-in, you can then opt-out with MVNW_SKIP_JDK.

Maybe I'm missing something here. Who is that has to opt-in?

If I download an open source project that uses Maven Wrapper and then run it with mvnw do I have to opt-in for the download of a JDK (jdkDistribution/jdkDistributionUrl) to be enabled?

  • Is there a way that this could be integrated with SDKMan for users that want that?

Not sure. That was really my first attempt. Unfortunately, sdkman does not keep track of old releases, which is a problem from a reproducibility pov.

Agreed. Yes, it is.

  • Have you considered security concerns with the distribution url? A person might check the source code, the POM etc for security stuff but fail to understand the implication of a distibution url that leads to a malicious JDK? I reckon if you use jdkDistribution then it uses the Foojay Disco API which seems to be some sort of assurance of the integrity of that JDK.

I agree with Jeremy here. A simple to secure things is to use an exact url + sha256.

See my use-case above. I don't see how an exact url and an sha256 in an open source project's maven-wrapper.properties helps.

If you want to keep some versatility, another way would be to be able to provide an alternative REST endpoint that could be implemented within an entreprise. At runtime, only https://api.foojay.io/disco/v3.0/packages is actually used. So an env var could specify a REST endpoint different than the default https://api.foojay.io/disco/v3.0.

Yes, I saw that that was added. But, I don't think that it's relevant to the security use-case I wrote above - unless I miss understood something.

jimisola avatar Jul 25 '25 09:07 jimisola

@gnodet After giving this some more though, the feature is great but I'm actually leaning towards that this feature is in the wrong place.

The Maven Wrapper project is just that "it wraps Maven" (and the name implies just that) - not JDKs. What this PR is doing is wrapping the JDK.

I don't think it obvious from a user perspective that a Maven Wrapper also wraps the JDK (and it never will be with that name) and I'm not sure that the two should be mixed. It's convenient but is it the right place?

Perhaps, there needs to be a JDK Wrapper? I get a sense of "virtual environments" with Maven Wrapper and JDK Wrapper. Perhaps, the two should be merged into something new that is not Maven Wrapper?

jimisola avatar Jul 25 '25 09:07 jimisola

If I download an open source project that uses Maven Wrapper and then run it with mvnw do I have to opt-in for the download of a JDK (jdkDistribution/jdkDistributionUrl) to be enabled?

You could just use your own Maven installation instead of using the projects embedded mvnw script and not have to worry about any of this. It's not as if the only way to run Maven is via the wrapper...

norrisjeremy avatar Jul 25 '25 09:07 norrisjeremy

If I download an open source project that uses Maven Wrapper and then run it with mvnw do I have to opt-in for the download of a JDK (jdkDistribution/jdkDistributionUrl) to be enabled?

You could just use your own Maven installation instead of using the projects embedded mvnw script and not have to worry about any of this. It's not as if the only way to run Maven is via the wrapper...

Yes, I can, but you are missing the point completely.

Maven Wrapper was created so that I would have to think of what Maven version to use with the project. Simply use mvnw and you're set.

Your "suggestion"/"workaround" is because the original functionality of Maven Wrapper is "broken".

jimisola avatar Jul 25 '25 09:07 jimisola

I agree with Jeremy here. A simple to secure things is to use an exact url + sha256.

I do wonder: how would the checksum pining work for multi-arch / multi-OS? Is it possible to specify multiple checksums so that a user could specify checksums for differing build environments (i.e. Windows x64, macOS arm64, Linux x64, etc.)?

norrisjeremy avatar Jul 25 '25 10:07 norrisjeremy

I sort of surprised that an enterprise that is that particular about security would use maven-wrapper in the first place, but to each their own. ๐Ÿคทโ€โ™‚๏ธ

It's not a matter of Enterprise or not. When I develop on my spare time I don't necessarily want to have a bunch of JDKs downloaded to my computer without my knowledge.

Then you can easily opt-out by default by adding export MVNW_SKIP_JDK=true in your shell config.

gnodet avatar Jul 25 '25 10:07 gnodet