Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,30 @@
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import io.opentelemetry.javaagent.instrumentation.rmi.context.client.RmiClientContextInstrumentation;
import io.opentelemetry.javaagent.instrumentation.rmi.context.server.RmiServerContextInstrumentation;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@AutoService(InstrumentationModule.class)
public class RmiContextPropagationInstrumentationModule extends InstrumentationModule {
public class RmiContextPropagationInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public RmiContextPropagationInstrumentationModule() {
super("rmi", "rmi-context-propagation");
}

@Override
public boolean isIndyModule() {
// java.lang.IllegalAccessError: class
// io.opentelemetry.javaagent.instrumentation.rmi.context.client.RmiClientContextInstrumentation$StreamRemoteCallConstructorAdvice (in unnamed module @0x740ee00f) cannot access class sun.rmi.transport.Connection (in module java.rmi) because module java.rmi does not export sun.rmi.transport to unnamed module @0x740ee00f
return false;
public List<TypeInstrumentation> typeInstrumentations() {
return asList(new RmiClientContextInstrumentation(), new RmiServerContextInstrumentation());
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(new RmiClientContextInstrumentation(), new RmiServerContextInstrumentation());
public Map<String, List<String>> jpmsModulesToOpen() {
String witnessClass = "sun.rmi.transport.StreamRemoteCall";
return Collections.singletonMap(
witnessClass, Arrays.asList("sun.rmi.server", "sun.rmi.transport"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.opentelemetry.javaagent.extension.instrumentation.internal.injection.ClassInjector;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
Expand Down Expand Up @@ -61,4 +62,17 @@ default String getModuleGroup() {
default List<String> agentPackagesToHide() {
return Collections.emptyList();
}

/**
* Some instrumentation need to access JPMS modules that are not accessible by default, this
* method provides a way to access those classes like the "--add-opens" JVM command. <br>
* Map key is the name of a "witness class" belonging to the module that will be loaded and used
* to get a reference to the module. <br>
* Map value is a list of packages to open in the module
*
* @return map of "witness class" FQN as key, list of packages as value.
*/
default Map<String, List<String>> jpmsModulesToOpen() {
return Collections.emptyMap();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.tooling;

import static java.util.logging.Level.FINE;
import static java.util.logging.Level.WARNING;

import java.lang.instrument.Instrumentation;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

public class ModuleOpener {

private static final Logger logger = Logger.getLogger(ModuleOpener.class.getName());

private ModuleOpener() {}

/**
* Opens JPMS module to a class loader unnamed module
*
* @param classFromTargetModule class from target module
* @param openTo class loader to open module for
* @param packagesToOpen packages to open
*/
public static void open(
Instrumentation instrumentation,
Class<?> classFromTargetModule,
ClassLoader openTo,
Collection<String> packagesToOpen) {

Module targetModule = classFromTargetModule.getModule();
Module openToModule = openTo.getUnnamedModule();
Set<Module> openToModuleSet = Collections.singleton(openToModule);
Map<String, Set<Module>> missingOpens = new HashMap<>();
for (String packageName : packagesToOpen) {
if (!targetModule.isOpen(packageName, openToModule)) {
missingOpens.put(packageName, openToModuleSet);
logger.log(
FINE,
() ->
String.format(
"Exposing package '%s' in module '%s' to module '%s'",
packageName, targetModule, openToModule));
}
}
if (missingOpens.isEmpty()) {
return;
}

if (!instrumentation.isModifiableModule(targetModule)) {
logger.log(WARNING, "Module '{}' can't be modified", targetModule);
return;
}

try {
instrumentation.redefineModule(
targetModule,
Collections.<Module>emptySet(), // reads
Collections.<String, Set<Module>>emptyMap(), // exports
missingOpens, // opens
Collections.<Class<?>>emptySet(), // uses
Collections.<Class<?>, List<Class<?>>>emptyMap() // provides
);
} catch (Exception e) {
logger.log(WARNING, "unable to redefine module", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@

package io.opentelemetry.javaagent.tooling.instrumentation.indy;

import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import io.opentelemetry.javaagent.tooling.ModuleOpener;
import io.opentelemetry.javaagent.tooling.util.ClassLoaderValue;
import java.lang.instrument.Instrumentation;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.bytebuddy.agent.builder.AgentBuilder;
Expand Down Expand Up @@ -65,6 +68,40 @@ public static InstrumentationModuleClassLoader getInstrumentationClassLoader(
+ " yet");
}

if (module instanceof ExperimentalInstrumentationModule) {
ExperimentalInstrumentationModule experimentalModule =
(ExperimentalInstrumentationModule) module;

// Opening JPMS modules requires to use a 'witness class' in the target module to get a
// reference to the module, which means we have to eagerly load the class.
//
// However, this code here triggered when the advice is being executed for the first time so
// this only creates a very small eager loading that is unlikely to have impact on the
// application.
//
// Also, using a class that is already loaded like the one that is being instrumented or a
// related one would increase the likeliness of not having an effect on application class
// loading.

Instrumentation instrumentation = InstrumentationHolder.getInstrumentation();
if (instrumentation == null) {
throw new IllegalStateException("global instrumentation not available");
}

experimentalModule
.jpmsModulesToOpen()
.forEach(
(className, packages) -> {
Class<?> type;
try {
type = Class.forName(className, false, instrumentedClassLoader);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("missing witness class " + className, e);
}

ModuleOpener.open(instrumentation, type, loader, packages);
});
}
return loader;
}

Expand Down
Loading