Skip to content

Linkage Checker Enforcer Rule Tutorial Problem Diagnosis

Tomo Suzuki edited this page Sep 6, 2019 · 1 revision

Diagnosis of NoSuchMethodError

Why did the NoSuchMethodError occur in the tutorial?

Dependency Graph

Let’s look at dependency graph. Maven has built-in 'dependency' plugin to show the graph.

$ mvn dependency:tree -Dverbose=true
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ no-such-method-error-example ---
[INFO] com.google.cloud.tools.opensource:no-such-method-error-example:jar:1.0-SNAPSHOT
[INFO] +- com.google.api-client:google-api-client:jar:1.27.0:compile
[INFO] |  +- com.google.oauth-client:google-oauth-client:jar:1.27.0:compile
[INFO] |  |  +- com.google.http-client:google-http-client:jar:1.27.0:compile
[INFO] |  |  |  +- (com.google.code.findbugs:jsr305:jar:3.0.2:compile - omitted for duplicate)
[INFO] |  |  |  +- (com.google.guava:guava:jar:20.0:compile - omitted for duplicate)
[INFO] |  |  |  +- org.apache.httpcomponents:httpclient:jar:4.5.5:compile
[INFO] |  |  |  |  +- org.apache.httpcomponents:httpcore:jar:4.4.9:compile
[INFO] |  |  |  |  +- commons-logging:commons-logging:jar:1.2:compile
[INFO] |  |  |  |  \- commons-codec:commons-codec:jar:1.10:compile
[INFO] |  |  |  \- com.google.j2objc:j2objc-annotations:jar:1.1:compile
[INFO] |  |  +- (com.google.code.findbugs:jsr305:jar:3.0.2:compile - omitted for duplicate)
[INFO] |  |  \- (com.google.guava:guava:jar:20.0:compile - omitted for duplicate)
[INFO] |  +- com.google.http-client:google-http-client-jackson2:jar:1.27.0:compile
[INFO] |  |  +- (com.google.http-client:google-http-client:jar:1.27.0:compile - omitted for duplicate)
[INFO] |  |  \- com.fasterxml.jackson.core:jackson-core:jar:2.9.6:compile
[INFO] |  \- com.google.guava:guava:jar:20.0:compile
[INFO] \- io.grpc:grpc-core:jar:1.17.1:compile
[INFO]    +- io.grpc:grpc-context:jar:1.17.1:compile
[INFO]    +- com.google.code.gson:gson:jar:2.7:compile
[INFO]    +- com.google.errorprone:error_prone_annotations:jar:2.2.0:compile
[INFO]    +- com.google.code.findbugs:jsr305:jar:3.0.2:compile
[INFO]    +- org.codehaus.mojo:animal-sniffer-annotations:jar:1.17:compile
[INFO]    +- (com.google.guava:guava:jar:26.0-android:compile - omitted for conflict with 20.0)
[INFO]    +- io.opencensus:opencensus-api:jar:0.17.0:compile
[INFO]    \- io.opencensus:opencensus-contrib-grpc-metrics:jar:0.17.0:compile
[INFO]       \- (io.opencensus:opencensus-api:jar:0.17.0:compile - omitted for duplicate)

The output shows it selected com.google.guava:guava:jar:20.0:compile, which is a dependency of com.google.api-client:google-api-client:jar:1.27.0:compile. It also shows (com.google.guava:guava:jar:26.0-android:compile - omitted for conflict with 20.0) as a dependency of io.grpc:grpc-core:jar:1.17.1:compile. The message "omitted for conflict with 20.0" means Maven did not picked up this version "26.0-android" in favor of version "20.0". This process is called 'dependency mediation'. Maven documentation on Transitive Dependencies explains:

Maven picks the "nearest definition". That is, it uses the version of the closest dependency to your project in the tree of dependencies.

Difference between The Two Artifacts

Next, let's check the difference between guava version 20.0 and 26.0-android.

The first thing you notice is the suffix "-android". Recent Guava releases publish two flavor of builds: one is for JRE 8 (suffix "-jre") and the other is for JRE 7 and Android platform (suffix "-android") (Adding Guava to your build). The artifact com.google.api-client:google-api-client uses "-android" flavor because the artifact supports JRE 7.

What about the missing method com.google.common.base.Verify.verify(ZLjava/lang/String;Ljava/lang/Object;)V? The method signature is encoded in JVM-internal format. The table in 4.2. The Internal Form of Names helps you decode the message.

com.google.common.base.Verify.verify(ZLjava/lang/String;Ljava/lang/Object;)V

Is verify method in class com.google.common.base.Verify that takes arguments (boolean, String, Object) with return value void. So the error message means the JVM tried to find the following method but it could not find it in Verify class.

class com.google.common.base.Verify {
  void verify(boolean, java.lang.String, java.lang.Object) {...
}

javap command comes with JDK 8. The command lists all available methods in the class. Run the following command to find methods available in Verify class in 'guava-26.0-android.jar' in your Maven local repository.

$ javap -cp `find ~/.m2/repository/ -name 'guava-26.0-android.jar'` com.google.common.base.Verify
Compiled from "Verify.java"
public final class com.google.common.base.Verify {
  public static void verify(boolean);
  public static void verify(boolean, java.lang.String, java.lang.Object...);
  public static void verify(boolean, java.lang.String, char);
  public static void verify(boolean, java.lang.String, int);
  public static void verify(boolean, java.lang.String, long)
  public static void verify(boolean, java.lang.String, java.lang.Object);
...

The 6th method is the method we are looking for with regard to the NoSuchMethodError. In fact, when you run javap command with guava-20.0.jar, you can confirm the method is not implemented in that version:

$ javap -cp `find ~/.m2/repository/ -name 'guava-20.0.jar'` com.google.common.base.Verify
Compiled from "Verify.java"
public final class com.google.common.base.Verify {
  public static void verify(boolean);
  public static void verify(boolean, java.lang.String, java.lang.Object...);
  public static <T> T verifyNotNull(T);
  public static <T> T verifyNotNull(T, java.lang.String, java.lang.Object...);
}

In summary, the NoSuchMethodError occurred because io.grpc:grpc-core:jar:1.17.1 wanted to use the missing method but Maven's dependency mediation algorithm picked up guava version 20.0 which does not have the method on Verify class.