Răsfoiți Sursa

Test modular jdk (#4501)

* Test modular jdk

* review comment
Lauri Tulmin 3 ani în urmă
părinte
comite
c109773f17

+ 7 - 0
instrumentation/internal/internal-lambda-java9/javaagent/build.gradle.kts

@@ -0,0 +1,7 @@
+plugins {
+  id("otel.javaagent-instrumentation")
+}
+
+otelJava {
+  minJavaVersionSupported.set(JavaVersion.VERSION_1_9)
+}

+ 30 - 0
instrumentation/internal/internal-lambda-java9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/Java9LambdaTransformer.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.internal.lambda;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+
+/** Helper class for transforming lambda class bytes using java9 api. */
+public final class Java9LambdaTransformer {
+
+  private Java9LambdaTransformer() {}
+
+  public static byte[] transform(
+      ClassFileTransformer transformer,
+      byte[] classBytes,
+      String slashClassName,
+      Class<?> targetClass)
+      throws IllegalClassFormatException {
+    return transformer.transform(
+        targetClass.getModule(),
+        targetClass.getClassLoader(),
+        slashClassName,
+        null,
+        null,
+        classBytes);
+  }
+}

+ 1 - 0
instrumentation/internal/internal-lambda/javaagent/build.gradle.kts

@@ -4,6 +4,7 @@ plugins {
 
 dependencies {
   compileOnly(project(":javaagent-bootstrap"))
+  implementation(project(":instrumentation:internal:internal-lambda-java9:javaagent"))
 
   testImplementation(project(":javaagent-bootstrap"))
 }

+ 9 - 2
instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java

@@ -10,7 +10,9 @@ import static java.util.Collections.singletonList;
 import com.google.auto.service.AutoService;
 import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
 import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
+import java.util.ArrayList;
 import java.util.List;
+import net.bytebuddy.utility.JavaModule;
 
 @AutoService(InstrumentationModule.class)
 public class LambdaInstrumentationModule extends InstrumentationModule {
@@ -28,8 +30,13 @@ public class LambdaInstrumentationModule extends InstrumentationModule {
   public List<String> getAdditionalHelperClassNames() {
     // this instrumentation uses ASM not ByteBuddy so muzzle doesn't automatically add helper
     // classes
-    return singletonList(
-        "io.opentelemetry.javaagent.instrumentation.internal.lambda.LambdaTransformer");
+    List<String> classNames = new ArrayList<>();
+    classNames.add("io.opentelemetry.javaagent.instrumentation.internal.lambda.LambdaTransformer");
+    if (JavaModule.isSupported()) {
+      classNames.add(
+          "io.opentelemetry.javaagent.instrumentation.internal.lambda.Java9LambdaTransformer");
+    }
+    return classNames;
   }
 
   @Override

+ 21 - 3
instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaTransformer.java

@@ -10,20 +10,38 @@ import java.lang.instrument.ClassFileTransformer;
 
 /** Helper class for transforming lambda class bytes. */
 public final class LambdaTransformer {
+  private static final boolean IS_JAVA_9 = isJava9();
 
   private LambdaTransformer() {}
 
+  private static boolean isJava9() {
+    try {
+      Class.forName("java.lang.Module", false, null);
+      return true;
+    } catch (ClassNotFoundException exception) {
+      return false;
+    }
+  }
+
   /**
    * Called from {@code java.lang.invoke.InnerClassLambdaMetafactory} to transform lambda class
    * bytes.
    */
+  @SuppressWarnings("unused")
   public static byte[] transform(byte[] classBytes, String slashClassName, Class<?> targetClass) {
     ClassFileTransformer transformer = ClassFileTransformerHolder.getClassFileTransformer();
     if (transformer != null) {
       try {
-        byte[] result =
-            transformer.transform(
-                targetClass.getClassLoader(), slashClassName, null, null, classBytes);
+        byte[] result;
+        if (IS_JAVA_9) {
+          result =
+              Java9LambdaTransformer.transform(
+                  transformer, classBytes, slashClassName, targetClass);
+        } else {
+          result =
+              transformer.transform(
+                  targetClass.getClassLoader(), slashClassName, null, null, classBytes);
+        }
         if (result != null) {
           return result;
         }

+ 54 - 0
javaagent-tooling/javaagent-tooling-java9/src/main/java/io/opentelemetry/javaagent/tooling/ExposeAgentBootstrapListener.java

@@ -0,0 +1,54 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.tooling;
+
+import io.opentelemetry.javaagent.bootstrap.AgentClassLoader;
+import java.lang.instrument.Instrumentation;
+import java.util.Collections;
+import net.bytebuddy.agent.builder.AgentBuilder;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.DynamicType;
+import net.bytebuddy.dynamic.loading.ClassInjector;
+import net.bytebuddy.utility.JavaModule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Ensures that transformed classes can read agent classes in bootstrap class loader. */
+public class ExposeAgentBootstrapListener extends AgentBuilder.Listener.Adapter {
+  private static final Logger logger = LoggerFactory.getLogger(ExposeAgentBootstrapListener.class);
+  // unnamed module in bootstrap class loader
+  private static final JavaModule agentBootstrapModule =
+      JavaModule.of(AgentClassLoader.class.getModule());
+
+  private final Instrumentation instrumentation;
+
+  public ExposeAgentBootstrapListener(Instrumentation instrumentation) {
+    this.instrumentation = instrumentation;
+  }
+
+  @SuppressWarnings("ReferenceEquality")
+  @Override
+  public void onTransformation(
+      TypeDescription typeDescription,
+      ClassLoader classLoader,
+      JavaModule javaModule,
+      boolean b,
+      DynamicType dynamicType) {
+    if (javaModule != JavaModule.UNSUPPORTED
+        && javaModule.isNamed()
+        && !javaModule.canRead(agentBootstrapModule)) {
+      logger.debug("Adding module read from {} to {}", javaModule, agentBootstrapModule);
+      ClassInjector.UsingInstrumentation.redefineModule(
+          instrumentation,
+          javaModule,
+          Collections.singleton(agentBootstrapModule),
+          Collections.emptyMap(),
+          Collections.emptyMap(),
+          Collections.emptySet(),
+          Collections.emptyMap());
+    }
+  }
+}

+ 5 - 40
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java

@@ -141,6 +141,9 @@ public class AgentInstaller {
             .with(AgentTooling.poolStrategy())
             .with(new ClassLoadListener())
             .with(AgentTooling.locationStrategy(Utils.getBootstrapProxy()));
+    if (JavaModule.isSupported()) {
+      agentBuilder = agentBuilder.with(new ExposeAgentBootstrapListener(inst));
+    }
 
     agentBuilder = configureIgnoredTypes(config, agentBuilder);
 
@@ -287,7 +290,7 @@ public class AgentInstaller {
         int amount, List<Class<?>> types, Map<List<Class<?>>, Throwable> failures) {}
   }
 
-  static class TransformLoggingListener implements AgentBuilder.Listener {
+  static class TransformLoggingListener extends AgentBuilder.Listener.Adapter {
 
     private static final TransformSafeLogger logger =
         TransformSafeLogger.getLogger(TransformLoggingListener.class);
@@ -317,21 +320,6 @@ public class AgentInstaller {
         DynamicType dynamicType) {
       logger.debug("Transformed {} -- {}", typeDescription.getName(), classLoader);
     }
-
-    @Override
-    public void onIgnored(
-        TypeDescription typeDescription,
-        ClassLoader classLoader,
-        JavaModule module,
-        boolean loaded) {}
-
-    @Override
-    public void onComplete(
-        String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {}
-
-    @Override
-    public void onDiscovery(
-        String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {}
   }
 
   /**
@@ -388,30 +376,7 @@ public class AgentInstaller {
     }
   }
 
-  private static class ClassLoadListener implements AgentBuilder.Listener {
-    @Override
-    public void onDiscovery(
-        String typeName, ClassLoader classLoader, JavaModule javaModule, boolean b) {}
-
-    @Override
-    public void onTransformation(
-        TypeDescription typeDescription,
-        ClassLoader classLoader,
-        JavaModule javaModule,
-        boolean b,
-        DynamicType dynamicType) {}
-
-    @Override
-    public void onIgnored(
-        TypeDescription typeDescription,
-        ClassLoader classLoader,
-        JavaModule javaModule,
-        boolean b) {}
-
-    @Override
-    public void onError(
-        String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {}
-
+  private static class ClassLoadListener extends AgentBuilder.Listener.Adapter {
     @Override
     public void onComplete(
         String typeName, ClassLoader classLoader, JavaModule javaModule, boolean b) {

+ 1 - 0
settings.gradle.kts

@@ -121,6 +121,7 @@ include(":instrumentation:internal:internal-class-loader:javaagent")
 include(":instrumentation:internal:internal-class-loader:javaagent-integration-tests")
 include(":instrumentation:internal:internal-eclipse-osgi-3.6:javaagent")
 include(":instrumentation:internal:internal-lambda:javaagent")
+include(":instrumentation:internal:internal-lambda-java9:javaagent")
 include(":instrumentation:internal:internal-proxy:javaagent")
 include(":instrumentation:internal:internal-proxy:javaagent-unit-tests")
 include(":instrumentation:internal:internal-reflection:javaagent")

+ 22 - 0
smoke-tests/src/test/groovy/io/opentelemetry/smoketest/JettyJpmsSmokeTest.groovy

@@ -0,0 +1,22 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.smoketest
+
+// jetty test with java module system
+@AppServer(version = "11.0.7", jdk = "11")
+class JettyJpmsSmokeTest extends AppServerTest {
+
+  @Override
+  protected String getTargetImagePrefix() {
+    "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-servlet-jetty"
+  }
+
+  @Override
+  protected String[] getCommand() {
+    // --jpms flags enables using java module system
+    return ["java", "-jar", "/server/start.jar", "--jpms"]
+  }
+}

+ 5 - 1
smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy

@@ -67,7 +67,7 @@ abstract class SmokeTest extends Specification {
 
   def startTarget(String jdk, String serverVersion, boolean windows) {
     def targetImage = getTargetImage(jdk, serverVersion, windows)
-    return containerManager.startTarget(targetImage, agentPath, jvmArgsEnvVarName, extraEnv, extraResources, getWaitStrategy())
+    return containerManager.startTarget(targetImage, agentPath, jvmArgsEnvVarName, extraEnv, extraResources, getWaitStrategy(), getCommand())
   }
 
   protected abstract String getTargetImage(String jdk)
@@ -80,6 +80,10 @@ abstract class SmokeTest extends Specification {
     return null
   }
 
+  protected String[] getCommand() {
+    return null
+  }
+
   def cleanup() {
     telemetryRetriever.clearTelemetry()
   }

+ 7 - 1
smoke-tests/src/test/java/io/opentelemetry/smoketest/LinuxTestContainerManager.java

@@ -69,7 +69,8 @@ public class LinuxTestContainerManager extends AbstractTestContainerManager {
       String jvmArgsEnvVarName,
       Map<String, String> extraEnv,
       List<ResourceMapping> extraResources,
-      TargetWaitStrategy waitStrategy) {
+      TargetWaitStrategy waitStrategy,
+      String[] command) {
 
     Consumer<OutputFrame> output = new ToStringConsumer();
     target =
@@ -97,6 +98,11 @@ public class LinuxTestContainerManager extends AbstractTestContainerManager {
                     .withStartupTimeout(waitStrategy.timeout));
       }
     }
+
+    if (command != null) {
+      target = target.withCommand(command);
+    }
+
     target.start();
     return output;
   }

+ 2 - 1
smoke-tests/src/test/java/io/opentelemetry/smoketest/TestContainerManager.java

@@ -24,7 +24,8 @@ public interface TestContainerManager {
       String jvmArgsEnvVarName,
       Map<String, String> extraEnv,
       List<ResourceMapping> extraResources,
-      TargetWaitStrategy waitStrategy);
+      TargetWaitStrategy waitStrategy,
+      String[] command);
 
   void stopTarget();
 }

+ 17 - 12
smoke-tests/src/test/java/io/opentelemetry/smoketest/windows/WindowsTestContainerManager.java

@@ -133,7 +133,8 @@ public class WindowsTestContainerManager extends AbstractTestContainerManager {
       String jvmArgsEnvVarName,
       Map<String, String> extraEnv,
       List<ResourceMapping> extraResources,
-      TargetWaitStrategy waitStrategy) {
+      TargetWaitStrategy waitStrategy,
+      String[] cmd) {
     stopTarget();
 
     if (!imageExists(targetImageName)) {
@@ -148,17 +149,21 @@ public class WindowsTestContainerManager extends AbstractTestContainerManager {
     target =
         startContainer(
             targetImageName,
-            command ->
-                command
-                    .withExposedPorts(ExposedPort.tcp(TARGET_PORT))
-                    .withHostConfig(
-                        HostConfig.newHostConfig()
-                            .withAutoRemove(true)
-                            .withNetworkMode(natNetworkId)
-                            .withPortBindings(
-                                new PortBinding(
-                                    new Ports.Binding(null, null), ExposedPort.tcp(TARGET_PORT))))
-                    .withEnv(environment),
+            command -> {
+              command
+                  .withExposedPorts(ExposedPort.tcp(TARGET_PORT))
+                  .withHostConfig(
+                      HostConfig.newHostConfig()
+                          .withAutoRemove(true)
+                          .withNetworkMode(natNetworkId)
+                          .withPortBindings(
+                              new PortBinding(
+                                  new Ports.Binding(null, null), ExposedPort.tcp(TARGET_PORT))))
+                  .withEnv(environment);
+              if (cmd != null) {
+                command.withCmd(cmd);
+              }
+            },
             containerId -> {
               try (InputStream agentFileStream = new FileInputStream(agentPath)) {
                 copyFileToContainer(