content: /*
   * Copyright The OpenTelemetry Authors
   * SPDX-License-Identifier: Apache-2.0
   */
  
  package io.opentelemetry.javaagent.tooling;
  
  import static java.util.logging.Level.FINE;
  
  import io.opentelemetry.javaagent.bootstrap.AgentClassLoader;
  import java.lang.instrument.Instrumentation;
  import java.util.Collections;
  import java.util.logging.Logger;
  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;
  
  /**
   * Ensures that transformed classes can read agent classes in bootstrap class loader and injected
   * classes in unnamed module of their class loader.
   */
  public class ExposeAgentBootstrapListener extends AgentBuilder.Listener.Adapter {
    private static final Logger logger =
        Logger.getLogger(ExposeAgentBootstrapListener.class.getName());
  
    // 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;
    }
  
    @Override
    public void onTransformation(
        TypeDescription typeDescription,
        ClassLoader classLoader,
        JavaModule javaModule,
        boolean b,
        DynamicType dynamicType) {
      // expose agent classes in unnamed module of bootstrap class loader
      exposeModule(javaModule, agentBootstrapModule);
      if (classLoader != null) {
        // expose classes in unnamed module of current class loader
        // this is needed so that advice code can access injected helper classes
        exposeModule(javaModule, JavaModule.of(classLoader.getUnnamedModule()));
      }
    }
  
    private void exposeModule(JavaModule fromModule, JavaModule targetModule) {
      if (fromModule != JavaModule.UNSUPPORTED
          && fromModule.isNamed()
          && !fromModule.canRead(targetModule)) {
  
        if (logger.isLoggable(FINE)) {
          logger.log(
              FINE, "Adding module read from {0} to {1}", new Object[] {fromModule, targetModule});
        }
  
        ClassInjector.UsingInstrumentation.redefineModule(
            instrumentation,
            fromModule,
            Collections.singleton(targetModule),
            Collections.emptyMap(),
            Collections.emptyMap(),
            Collections.emptySet(),
            Collections.emptyMap());
      }
    }
  }
  