Browse Source

Add Android API-friendliness checks (#4505)

* Add Android API-friendliness checks

* Improve comments

* Remove ignores

* Handle CompletionException

* Spotless

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
Mateusz Rzeszutek 3 years ago
parent
commit
9a4a68d836

+ 1 - 0
conventions/build.gradle.kts

@@ -46,6 +46,7 @@ dependencies {
   implementation("org.ow2.asm:asm-tree:9.1")
   implementation("org.apache.httpcomponents:httpclient:4.5.13")
   implementation("org.gradle:test-retry-gradle-plugin:1.2.1")
+  implementation("ru.vyarus:gradle-animalsniffer-plugin:1.5.3")
   // When updating, also update dependencyManagement/build.gradle.kts
   implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.11.22")
   implementation("gradle.plugin.io.morethan.jmhreport:gradle-jmh-report:0.9.0")

+ 13 - 0
conventions/src/main/kotlin/otel.animalsniffer-conventions.gradle.kts

@@ -0,0 +1,13 @@
+plugins {
+  `java-library`
+
+  id("ru.vyarus.animalsniffer")
+}
+
+dependencies {
+  add("signature", "com.toasttab.android:gummy-bears-api-21:0.3.0:coreLib@signature")
+}
+
+animalsniffer {
+  sourceSets = listOf(java.sourceSets.main.get())
+}

+ 1 - 0
instrumentation-api/build.gradle.kts

@@ -2,6 +2,7 @@ plugins {
   id("org.xbib.gradle.plugin.jflex")
 
   id("otel.java-conventions")
+  id("otel.animalsniffer-conventions")
   id("otel.jacoco-conventions")
   id("otel.japicmp-conventions")
   id("otel.publish-conventions")

+ 19 - 2
instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/JdkErrorCauseExtractor.java

@@ -7,17 +7,20 @@ package io.opentelemetry.instrumentation.api.instrumenter;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.UndeclaredThrowableException;
-import java.util.concurrent.CompletionException;
 import java.util.concurrent.ExecutionException;
+import javax.annotation.Nullable;
 
 final class JdkErrorCauseExtractor implements ErrorCauseExtractor {
   static final ErrorCauseExtractor INSTANCE = new JdkErrorCauseExtractor();
 
+  @Nullable
+  private static final Class<?> COMPLETION_EXCEPTION_CLASS = getCompletionExceptionClass();
+
   @Override
   public Throwable extractCause(Throwable error) {
     if (error.getCause() != null
         && (error instanceof ExecutionException
-            || error instanceof CompletionException
+            || isInstanceOfCompletionException(error)
             || error instanceof InvocationTargetException
             || error instanceof UndeclaredThrowableException)) {
       return extractCause(error.getCause());
@@ -25,5 +28,19 @@ final class JdkErrorCauseExtractor implements ErrorCauseExtractor {
     return error;
   }
 
+  private static boolean isInstanceOfCompletionException(Throwable error) {
+    return COMPLETION_EXCEPTION_CLASS != null && COMPLETION_EXCEPTION_CLASS.isInstance(error);
+  }
+
+  @Nullable
+  private static Class<?> getCompletionExceptionClass() {
+    try {
+      return Class.forName("java.util.concurrent.CompletionException");
+    } catch (ClassNotFoundException e) {
+      // Android level 21 does not support java.util.concurrent.CompletionException
+      return null;
+    }
+  }
+
   private JdkErrorCauseExtractor() {}
 }

+ 19 - 2
instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/tracer/BaseTracer.java

@@ -22,9 +22,9 @@ import io.opentelemetry.instrumentation.api.internal.ContextPropagationDebug;
 import io.opentelemetry.instrumentation.api.internal.SupportabilityMetrics;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.UndeclaredThrowableException;
-import java.util.concurrent.CompletionException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
 
 /**
  * Base class for all instrumentation specific tracer implementations.
@@ -48,6 +48,9 @@ import java.util.concurrent.TimeUnit;
 public abstract class BaseTracer {
   private static final SupportabilityMetrics supportability = SupportabilityMetrics.instance();
 
+  @Nullable
+  private static final Class<?> COMPLETION_EXCEPTION_CLASS = getCompletionExceptionClass();
+
   private final Tracer tracer;
   private final ContextPropagators propagators;
 
@@ -242,7 +245,7 @@ public abstract class BaseTracer {
   protected Throwable unwrapThrowable(Throwable throwable) {
     if (throwable.getCause() != null
         && (throwable instanceof ExecutionException
-            || throwable instanceof CompletionException
+            || isInstanceOfCompletionException(throwable)
             || throwable instanceof InvocationTargetException
             || throwable instanceof UndeclaredThrowableException)) {
       return unwrapThrowable(throwable.getCause());
@@ -283,4 +286,18 @@ public abstract class BaseTracer {
   public <C> void inject(Context context, C carrier, TextMapSetter<C> setter) {
     propagators.getTextMapPropagator().inject(context, carrier, setter);
   }
+
+  private static boolean isInstanceOfCompletionException(Throwable error) {
+    return COMPLETION_EXCEPTION_CLASS != null && COMPLETION_EXCEPTION_CLASS.isInstance(error);
+  }
+
+  @Nullable
+  private static Class<?> getCompletionExceptionClass() {
+    try {
+      return Class.forName("java.util.concurrent.CompletionException");
+    } catch (ClassNotFoundException e) {
+      // Android level 21 does not support java.util.concurrent.CompletionException
+      return null;
+    }
+  }
 }

+ 1 - 0
instrumentation/apache-httpclient/apache-httpclient-4.3/library/build.gradle.kts

@@ -1,6 +1,7 @@
 plugins {
   id("otel.library-instrumentation")
   id("otel.nullaway-conventions")
+  id("otel.animalsniffer-conventions")
 }
 
 dependencies {

+ 1 - 0
instrumentation/grpc-1.6/library/build.gradle.kts

@@ -1,5 +1,6 @@
 plugins {
   id("otel.library-instrumentation")
+  id("otel.animalsniffer-conventions")
 }
 
 val grpcVersion = "1.6.0"

+ 1 - 0
instrumentation/okhttp/okhttp-3.0/library/build.gradle.kts

@@ -1,6 +1,7 @@
 plugins {
   id("otel.library-instrumentation")
   id("otel.nullaway-conventions")
+  id("otel.animalsniffer-conventions")
 }
 
 dependencies {

+ 1 - 0
settings.gradle.kts

@@ -10,6 +10,7 @@ pluginManagement {
     id("org.xbib.gradle.plugin.jflex") version "1.5.0"
     id("nebula.release") version "15.3.1"
     id("com.github.johnrengelman.shadow") version "7.0.0"
+    id("ru.vyarus.animalsniffer") version "1.5.3"
   }
 }