Search code examples
javakubernetesgradlejvmjmx

Unable to connect visualvm to java application after 7.3 gradle upgrade


We recently upgraded gradle version in our project from 7.1.x to 7.3.x. We observed that we are no longer able to connect visualvm java(11) application running in kubernetes. Port-forward works but when visualvm connection is opened, port-forwarding stops. We found few blogs indicating a CVE fix in 7.2.x which says "Gradle 7.2 start script stopped expanding environment variables passed inside JAVA_OPTS".

https://github.com/gradle/gradle/issues/18170

https://github.com/gradle/gradle/security/advisories/GHSA-6j2p-252f-7mw8

Application gradle scripts:

We pass jvm parameters through gradle and artifacts is passed as entrypoint to docker image. What is the right way to pass jvm parameters after 7.2.x?

ext.getJvmArgs = { jmxPort, jdwpPort, initialRamPercentage = 50.0, maxRamPercentage = 70.0 ->
    return ["-XX:InitialRAMPercentage=$initialRamPercentage",
            "-XX:MaxRAMPercentage=$maxRamPercentage",
            "-XX:+UseStringDeduplication",
            // Force G1GC as the GC collector. In a container environment, GC is selected by docker based on CPU and Memory.
            "-XX:+UseG1GC",
            "-Duser.timezone=\"UTC\"",
            "-Dcom.sun.management.jmxremote",
            "-Dcom.sun.management.jmxremote.authenticate=false",
            "-Dcom.sun.management.jmxremote.ssl=false",
            "-Dcom.sun.management.jmxremote.local.only=false",
            "-Dcom.sun.management.jmxremote.port=$jmxPort",
            "-Dcom.sun.management.jmxremote.rmi.port=$jmxPort"]
}

application {
    int jmxPort = 2609
    int jdwpPort = 2611
    float initialRamPercentage = 30.0
    float maxRamPercentage = 60.0
    applicationDefaultJvmArgs = project.getJvmArgs(jmxPort, jdwpPort, initialRamPercentage, maxRamPercentage) + ['-XX:ErrorFile=/tmp/hs_err_pid%p.log']
    getMainClass().set('com.xxx.yyy.AbcMain')
}

task customScript(type: CreateStartScripts) {
    mainClass = "com.xxx.yyy.XyzMain"
    applicationName = "xyz"
    int jmxPort = 2609
    int jdwpPort = 2611
    float initialRamPercentage = 30.0
    float maxRamPercentage = 60.0
    defaultJvmOpts = project.getJvmArgs(jmxPort, jdwpPort, initialRamPercentage, maxRamPercentage) + ['-XX:ErrorFile=/tmp/hs_err_pid%p.log']
    outputDir = new File(project.buildDir, 'scripts')
    classpath = jar.outputs.files + configurations.runtimeClasspath
}

applicationDistribution.into("bin") {
    from(customScript)
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

Solution

  • Finally after spending good amount of time in debuging this, it was special character in parameters which was somehow working with gradle 7.1 and earlier. After gradle 7.2, all parameters inclduing user timezone was getting truncated.

    "-Duser.timezone=\"UTC\""
    

    After I removed double quotes and backslash, I could connect to visualvm and see all jvm parameters showing up in defaultJVmOpts

    Correct way of passing timezone:

    "-Duser.timezone=UTC"