Skip to content

Understanding the Problem

Elliotte Rusty Harold edited this page Jan 16, 2019 · 8 revisions

tldr; some of the libraries GCP publishes cannot be used together in the same project.

At worst, library A requires version 1 of library X and will not work with version 2 of library X. At the same time library B requires version 2 of library X and will not work with version 1. Thus no other project can use both A and B.

A slightly more common variant is that library A requires version 1 of library X but will work with version 2. Library B requires version 2 of library X and will not work with version 1. However the build path is configured such that version 1 is selected instead of version 2.

Is this a real problem? Yes. Most of the time A and B work perfectly well together, but not always; and when a problem like this does arise it's typically quite hard to diagnose and fix.

Here is one of the simplest examples we've found.

Start with this pom.xml. It has exactly two dependencies, com.google.api-client:google-api-client:1.27.0 and io.grpc:grpc-core:1.17.1. Both are official, supported GCP products. At the time of this writing these are the latest, most up-to-date versions of these artifacts.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.google.cloud.tools.opensource</groupId>
  <artifactId>no-such-method-error-example</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>no-such-method-error-example</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.google.api-client</groupId>
      <artifactId>google-api-client</artifactId>
      <version>1.27.0</version>
    </dependency>
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-core</artifactId>
      <version>1.17.1</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.6.0</version>
        <configuration>
          <mainClass>io.grpc.internal.App</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

We also need a single .java source file:

package io.grpc.internal;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.Random;

public class App {

  public static void main(String[] args) {
    Map<String, Object> choice = ImmutableMap.of("clientLanguage", ImmutableList.of("en"));
    DnsNameResolver.maybeChooseServiceConfig(choice, new Random(), "localhost");
  }
}

(You can find this project ready to run in the source repo.)

The project compiles without error. Now let's run it:

$ mvn exec:java
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building no-such-method-error-example 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ no-such-method-error-example ---
[WARNING] 
java.lang.NoSuchMethodError: com.google.common.base.Verify.verify(ZLjava/lang/String;Ljava/lang/Object;)V
	at io.grpc.internal.DnsNameResolver.maybeChooseServiceConfig(DnsNameResolver.java:514)
	at io.grpc.internal.App.main(App.java:31)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:282)
	at java.lang.Thread.run(Thread.java:748)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.800 s
[INFO] Finished at: 2019-01-16T14:34:56-05:00
[INFO] Final Memory: 9M/155M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.6.0:java (default-cli) on project no-such-method-error-example: An exception occured while executing the Java class. com.google.common.base.Verify.verify(ZLjava/lang/String;Ljava/lang/Object;)V -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

What happened? In brief, io.grpc:grpc-core:1.17.1 needs a particular overloaded verify method that is present in Guava 26.0. However com.google.api-client:google-api-client:1.27.0 instead pulls in Guava 20.0 which does not have this method. This is not discovered until runtime when something follows a code path that tries to call the missing method and fails.

Is this example too simple?

Clone this wiki locally