Toolchain support on Java8 and tests

I have an existing Gradle 7.4 multi-module project that is building on Java8. I am trying to convert the build to Java17 without having to change anything on my build server. The build server has Java8 installed and it’s included in the PATH env var. I’m trying to adopt toolchains to solve this. I have added the following to my top-level build.gradle:

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)

The hope was this was all I would have to do. However, I am seeing that my test task is failing with the following error:

-Djava.endorsed.dirs=/usr/lib/jvm/adoptopenjdk-8-openj9/jre/lib/endorsed is not supported. Endorsed standards and standalone APIs
in modular form will be supported via the concept of upgradeable modules.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Process 'Gradle Test Executor 5' finished with non-zero exit value 1
org.gradle.process.internal.ExecException: Process 'Gradle Test Executor 5' finished with non-zero exit value 1

I can see that the Java17 was indeed downloaded:

> Task :javaToolchains

 + Options
     | Auto-detection:     Enabled
     | Auto-download:      Enabled

 + AdoptOpenJDK 1.8.0_292-b10
     | Location:           /usr/lib/jvm/adoptopenjdk-8-openj9
     | Language Version:   8
     | Vendor:             AdoptOpenJDK
     | Architecture:       amd64
     | Is JDK:             true
     | Detected by:        Common Linux Locations
 + Eclipse Temurin JDK
     | Location:           /opt/teamcity/.gradle/jdks/jdk-
     | Language Version:   17
     | Vendor:             Eclipse Temurin
     | Architecture:       amd64
     | Is JDK:             true
     | Detected by:        Auto-provisioned by Gradle

It’s only the test task though. The compile seems to work. I’m using JUnit4 if that matters.

It seems your build script or some plugin you use sets -Djava.endorsed.dirs=/usr/lib/jvm/adoptopenjdk-8-openj9/jre/lib/endorsed on the test task. Setting this option is no longer supported after Java 8, so if you want to use any Java version 9 or later, you need to get rid of that setting.

So I think I may be on to something here. We have this defined in our init.grade file:

plugins.withType(JavaPlugin) {
        test {
            options {

I believe this is being done to support passing of -D parameters from the command line. How might we accomplish this differently?

ah ha… within the specific build where properties are needed, I can do this:

// specify the properties to pass through to the test execution - CANNOT pass all of them
integrationTest {
    doFirst {
        options {
            systemProperties System.getProperties().subMap([

I don’t love having to specify all of them, but this is at least workable. Perhaps we should be passing instead and using -P, not -D?

So I think I may be on to something here.

Yes, that seems to be the culprit.
It is usually not a good idea to do something like that.
Tests should not be influenced by what you specify on the commandline.
Tests should have their environment defined an then always and everywhere run consistently.
And if you really want to change the tests behavior from the commandline, I’d recommend to have explicit switches that are considered, for example by using project properties that you then forward to the test task as system properties for example.

I can do this

You really shouldn’t do it like that.
You now modify the configuration of the task at its execution time. That is always a bad idea. And it disturbs up-to-date checks. And with configuration cache it is also not supported, neither to change the own configuration, nor the configuration of a different task.
It also does not make much sense to do it at execution time, as system properties cannot change during build execution anyway, so there is no reason to not do it at configuration time already.

But besides that, this is basically the “forward only explicit ones” I mentioned above, so :ok_hand:.

But again regarding configuration cache an important point. Like you do it now, you make each and every system property an input for the configuration cache if you ever use it which is not a too good idea. Better get the individual properties and forward them, than to get the whole map and filtering it.

Perhaps we should be passing instead and using -P, not -D?

Up to you which you use, makes probably not too much difference in your case.

Btw. iirc the system properties on the test task are not automatically task inputs. Try to run with PROP2=a and then again with PROP2=b and you will probably get up-to-date, or a cache hit, unless you disabled caching and up-to-date checks. So you might need to also manually define these properties and their values as task inputs.