Try-with-resources should be used - Rule ID:java:S2093 - Potential False Negative - Java

Make sure to read this post before raising a thread here:

Then tell us:

  • What language is this for? > Java
  • Which rule? > Try-with-resources should be used - Rule ID:java:S2093
  • Why do you believe it’s a false-positive/false-negative?
  • Are you using
    • SonarQube Version v10.6 (92116)
  • How can we reproduce the problem? Give us a self-contained snippet of code (formatted text, no screenshots)
  • What build tool are you using? > Jenkins
  • What build plugin are you using? Maven SonarQube plugin version [3.3.0.603]

We recently discovered a resource leak in Java in our code that we believe should have been caught by the referenced rule, but wasn’t. Here is a code snippet that we believe should have been flagged:
False Negative Code (we believe)

import io.dropwizard.client.JerseyClientBuilder;
import io.dropwizard.setup.Environment;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/*
The following intentionally creates a potential resource leak by not properly handling the Autocloseable Response.
Sonar should flag this per Try-with-resources should be used - Rule ID:java:S2093
 */
public class MichaelBrokenCode {
    private final Client httpClient = new JerseyClientBuilder(new Environment("test")).build("example");
    public Response requestSomething() {
        final WebTarget target = httpClient.target("leak.io");
        return target.request(MediaType.APPLICATION_JSON_TYPE).get();
    }
    public String createLeak() {
        return requestSomething().getEntity().toString();
    }
}

We also looked into whether we had to set the Java version via the sonar.java.source parameter to get the rule to invoke and flag the code above. Historically we have set the Java version as detailed below in our JenkinsFile
JenkinsFile

    parameters {
        string(name: 'JDK_VERSION',
                   defaultValue: 'JDK21',
                   description: 'JDK version'
        )
...
                    steps {
                        withMaven(jdk:params.JDK_VERSION, options: [artifactsPublisher(disabled: true)]) {
                            script {
                                withSonarQubeEnv(credentialsId: 'sonarqube-token', installationName: 'sonarqube-main') {
                                    sh "./mvnw verify -T 1C -P ${params.MVN_PROFILE} sonar:sonar -Dskip.packaging=true -Dsonar.projectKey=platform --fail-never -Dorg.slf4j.simpleLogger.showThreadName=true -Dorg.slf4j.simpleLogger.showDateTime=true -Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss.SSS | tee mvn_test_out.txt"
                                    def grepStatus = sh script: "grep \"Build failures were ignored\" mvn_test_out.txt", returnStatus: true
                                    if (grepStatus == 0) {
                                        // grep exits zero if lines were found, nonzero otherwise
                                        currentBuild.result = 'UNSTABLE'
                                    }
                                }
                            }
                        }
                    }

that wasn’t flagging the issue so we tried adding the sonar.java.source property to the scan script:

   parameters {
        string(name: 'JDK_VERSION',
                   defaultValue: 'JDK21',
                   description: 'JDK version'
        )
...
                    steps {
                        withMaven(jdk:params.JDK_VERSION, options: [artifactsPublisher(disabled: true)]) {
                            script {
                                withSonarQubeEnv(credentialsId: 'sonarqube-token', installationName: 'sonarqube-main') {
                                    sh "./mvnw verify -T 1C -P ${params.MVN_PROFILE} sonar:sonar -Dskip.packaging=true -Dsonar.projectKey=platform -Dsonar.java.source=21 --fail-never -Dorg.slf4j.simpleLogger.showThreadName=true -Dorg.slf4j.simpleLogger.showDateTime=true -Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss.SSS | tee mvn_test_out.txt"
                                    def grepStatus = sh script: "grep \"Build failures were ignored\" mvn_test_out.txt", returnStatus: true
                                    if (grepStatus == 0) {
                                        // grep exits zero if lines were found, nonzero otherwise
                                        currentBuild.result = 'UNSTABLE'
                                    }
                                }
                            }
                        }
                    }

This also didn’t flag the issue. Thoughts?

Hey @Matt_Casperson,
Welcome to the community and thank you for reporting this issue!

The example you showed highlights 2 issues with S2093:

  1. The rule has a narrow scope of resources created within a try-catch block (
    S2093 was really meant to help developers migrate their explicit close() invocations from a finally block to a more modern and concise try-with-resources.)
  2. The rule fails to identify javax.ws.rs.core.Response as an Autocloseable type

I will need to investigate a little further to understand why both issues were tackled in the past are now falling through the cracks.

2 Likes

Hey @Dorian_Burihabwa wondering if you’ve had a chance to take a deeper look at this one? Thanks!

hey @Matt_Casperson,

I was looking into the issue yesterday, and I created a ticket related to your issue but will not completely solve it.

As I mentioned earlier, S2093 is meant to help migrate pre-Java 7 try-finally constructs to try-with-resources. For this reason, it should flag resources that are closed manually in a finally block.
Looking into resources that have not been closed, falls under S2095.

Unfortunately, S2095 relies on our old Java Symbolic Execution Engine, which is no longer under active development. We have quite a few documented false-negatives, and this is something we would like to approach in the future with a more comprehensive and maintainable manner.

Cheers,

Dorian