0

I'm facing this weird java.lang.NoSuchMethodError in my Groovy project, and I have it pretty much down to that there's some transitive grand-child dependency, that is being included by multiple child dependencies (below is just the compileClasspath):

compileClasspath - Compile classpath for source set 'main'.
+--- com.github.javafaker:javafaker:1.0.2
|    +--- org.apache.commons:commons-lang3:3.5
|    +--- org.yaml:snakeyaml:1.23
|    \--- com.github.mifmif:generex:1.0.2
|         \--- dk.brics.automaton:automaton:1.11-8
+--- com.google.apis:google-api-services-gmail:v1-rev20220404-2.0.0
|    \--- com.google.api-client:google-api-client:2.0.0
|         +--- com.google.oauth-client:google-oauth-client:1.34.1
|         |    _+--- com.google.http-client:google-http-client:1.42.0 -> 1.42.1_
|         |    |    +--- org.apache.httpcomponents:httpclient:4.5.13
|         |    |    |    +--- org.apache.httpcomponents:httpcore:4.4.13 -> 4.4.15
|         |    |    |    +--- commons-logging:commons-logging:1.2
|         |    |    |    \--- commons-codec:commons-codec:1.11
|         |    |    +--- org.apache.httpcomponents:httpcore:4.4.15
|         |    |    +--- com.google.code.findbugs:jsr305:3.0.2
|         |    |    +--- com.google.guava:guava:30.1.1-android -> 31.1-jre
|         |    |    |    +--- com.google.guava:failureaccess:1.0.1
|         |    |    |    +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|         |    |    |    +--- com.google.code.findbugs:jsr305:3.0.2
|         |    |    |    +--- org.checkerframework:checker-qual:3.12.0
|         |    |    |    +--- com.google.errorprone:error_prone_annotations:2.11.0
|         |    |    |    \--- com.google.j2objc:j2objc-annotations:1.3
|         |    |    +--- com.google.j2objc:j2objc-annotations:1.3
|         |    |    +--- io.opencensus:opencensus-api:0.31.1
|         |    |    |    \--- io.grpc:grpc-context:1.27.2
|         |    |    \--- io.opencensus:opencensus-contrib-http-util:0.31.1
|         |    |         +--- io.opencensus:opencensus-api:0.31.1 (*)
|         |    |         \--- com.google.guava:guava:29.0-android -> 31.1-jre (*)
|         |    +--- com.google.http-client:google-http-client-gson:1.42.0 -> 1.42.1
|         |    |    _+--- com.google.http-client:google-http-client:1.42.1 (*)_
|         |    |    \--- com.google.code.gson:gson:2.9.0
|         |    \--- com.google.guava:guava:31.1-android -> 31.1-jre (*)
|         +--- com.google.http-client:google-http-client-gson:1.42.1 (*)
|         +--- com.google.guava:guava:31.1-jre (*)
|         +--- com.google.http-client:google-http-client-apache-v2:1.42.1
|         |    _+--- com.google.http-client:google-http-client:1.42.1 (*)_
|         |    +--- org.apache.httpcomponents:httpclient:4.5.13 (*)
|         |    \--- org.apache.httpcomponents:httpcore:4.4.15
|         +--- org.apache.httpcomponents:httpcore:4.4.15
|         +--- org.apache.httpcomponents:httpclient:4.5.13 (*)
|         \--- _com.google.http-client:google-http-client:1.42.1 (*)_
\--- org.codehaus.groovy:groovy-all:2.4.7

My build.gradle looks like this :

plugins {
  id 'java'
  id "com.katalon.gradle-plugin" version "0.1.1"
}

repositories {
  mavenCentral()
}

dependencies {
  implementation 'com.github.javafaker:javafaker:1.0.2'
  
   implementation 'com.google.apis:google-api-services-gmail:v1-rev20220404-2.0.0'
}

Notice that there are multiple instances of this com.google.http-client:google-http-client:1.42.1 across different child dependencies!

I try to get rid of those transitive dependencies with:

configurations.all { 
    exclude group: 'com.google.http-client', module: 'google-http-client' 
}

but then my compileClasspath looks like:

compileClasspath - Compile classpath for source set 'main'.
+--- com.github.javafaker:javafaker:1.0.2
|    +--- org.apache.commons:commons-lang3:3.5
|    +--- org.yaml:snakeyaml:1.23
|    \--- com.github.mifmif:generex:1.0.2
|         \--- dk.brics.automaton:automaton:1.11-8
+--- com.google.apis:google-api-services-gmail:v1-rev20220404-2.0.0
|    \--- com.google.api-client:google-api-client:2.0.0
|         +--- com.google.oauth-client:google-oauth-client:1.34.1
|         |    +--- com.google.http-client:google-http-client-gson:1.42.0 -> 1.42.1
|         |    |    \--- com.google.code.gson:gson:2.9.0
|         |    \--- com.google.guava:guava:31.1-android -> 31.1-jre
|         |         +--- com.google.guava:failureaccess:1.0.1
|         |         +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|         |         +--- com.google.code.findbugs:jsr305:3.0.2
|         |         +--- org.checkerframework:checker-qual:3.12.0
|         |         +--- com.google.errorprone:error_prone_annotations:2.11.0
|         |         \--- com.google.j2objc:j2objc-annotations:1.3
|         +--- com.google.http-client:google-http-client-gson:1.42.1 (*)
|         +--- com.google.guava:guava:31.1-jre (*)
|         +--- com.google.http-client:google-http-client-apache-v2:1.42.1
|         |    +--- org.apache.httpcomponents:httpclient:4.5.13
|         |    |    +--- org.apache.httpcomponents:httpcore:4.4.13 -> 4.4.15
|         |    |    +--- commons-logging:commons-logging:1.2
|         |    |    \--- commons-codec:commons-codec:1.11
|         |    \--- org.apache.httpcomponents:httpcore:4.4.15
|         +--- org.apache.httpcomponents:httpcore:4.4.15
|         \--- org.apache.httpcomponents:httpclient:4.5.13 (*)
\--- org.codehaus.groovy:groovy-all:2.4.7

There's NO instances of the com.google.http-client:google-http-client:1.42.1 ANYWHERE! It also doesn't resolve the Error.

java.lang.NoSuchMethodError: com.google.api.client.http.HttpTransport.isMtls()Z
    at com.google.api.services.gmail.Gmail$Builder.chooseEndpoint(Gmail.java:11179)
    at com.google.api.services.gmail.Gmail$Builder.<init>(Gmail.java:11212)
    at com.signaturemd.utils.GmailQuickstart.GetLabels(GmailQuickstart.groovy:72)
    at com.signaturemd.utils.GmailQuickstart$GetLabels.call(Unknown Source)
    at SMDEmailUtils.run(SMDEmailUtils:6)

What should I do to make sure that only ONE instance of com.google.http-client:google-http-client:1.42.1 is in the project, and that this Error goes away?

Mike Warren
  • 3,796
  • 5
  • 47
  • 99

1 Answers1

1

When you are saying "multiple instances" of the dependency what do you mean? I would assume you are confused by seeing multiple versions of the same dependency in the dependency graph.

But that has nothing to do with how many physical instances (jar files) will be packed up into the resulting artifact of your own. There is always just one physical instance of any dependency in the resulting artifact managed by either Gradle or Maven. However, if there are multiple different versions of the same dependency in the dependency graph they both choose the most appropriate version of that one physical instance as they see fit:

In case of conflict, Gradle by default uses the newest of conflicting versions.

Dependency mediation - this determines what version of an artifact will be chosen when multiple versions are encountered as dependencies. Maven picks the "nearest definition". That is, it uses the version of the closest dependency to your project in the tree of dependencies. You can always guarantee a version by declaring it explicitly in your project's POM. Note that if two dependency versions are at the same depth in the dependency tree, the first declaration wins.

And you can override that version. So, in case of Gradle, instead of configuration.all { exclude {...} } that excludes the dependency from everywhere you can try configuring the resolutionStrategy like this:

configuration.all {
  resolutionStrategy {
    force "com.google.http-client:google-http-client:1.42.1"
  }
}

That would (snippet from the doc below):

Allows forcing certain versions of dependencies, including transitive dependencies

This is what you need as per your original question. Like obviously, you don't want to exclude the dependency but rather you want to enforce the specific version of it.

For more info you can refer to the official documentation:

Dmitry Khamitov
  • 3,061
  • 13
  • 21
  • I'm sorry, but I don't see how forcing the transitive dependency version is going to resolve this issue... – Mike Warren Oct 02 '22 at 12:24
  • @MikeWarren Have you read the docs and tried the solution? if you read the docs carefully you would see that it's not only about "forcing the transitive dependencies" but those are included as well. This is what you need as your own question states. Let me quote yourself here: "only ONE instance" of that dependency. Otherwise, the question is incorrect. – Dmitry Khamitov Oct 02 '22 at 12:33
  • @MikeWarren also, I've updated my answer to clarify your confusion about "multiple instances" of the dependency. – Dmitry Khamitov Oct 02 '22 at 12:46
  • I'm still facing the same Error... :'( – Mike Warren Oct 02 '22 at 13:55
  • @MikeWarren Are you sure you've removed the exclusion of that library? As far as I can see, the `isMtls` method was introduced in [1.38](https://github.com/googleapis/google-http-java-client/blob/6b9585b0539af6b4631d005a61bb2af60804453a/google-http-client/src/main/java/com/google/api/client/http/HttpTransport.java#L136). So, given you are using `1.42.1`, that method must be there. Also, when and where are you getting this error? In the tests of the same project? – Dmitry Khamitov Oct 02 '22 at 15:33
  • Yea, I'm sure. Not only did it not resolve the issue, but I saw no change to the output of `gradle clean dependencies` – Mike Warren Oct 02 '22 at 16:31
  • @MikeWarren there shouldn't be any change in the output if the version you force is the same for the default Gradle resolution strategy. I highly recommend you to read the docs on that. Plus, you didn't answer the question about about "where and when". The original post really lacks some crucial details. – Dmitry Khamitov Oct 02 '22 at 17:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248510/discussion-between-mike-warren-and-dmitry-khamitov). – Mike Warren Oct 02 '22 at 18:17