Skip to content

Commit 1a9a687

Browse files
committed
add module opener
1 parent dd05ff3 commit 1a9a687

File tree

4 files changed

+141
-1
lines changed

4 files changed

+141
-1
lines changed

instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/RmiContextPropagationInstrumentationModule.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@
1010
import com.google.auto.service.AutoService;
1111
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
1212
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
1314
import io.opentelemetry.javaagent.instrumentation.rmi.context.client.RmiClientContextInstrumentation;
1415
import io.opentelemetry.javaagent.instrumentation.rmi.context.server.RmiServerContextInstrumentation;
16+
import java.util.Arrays;
17+
import java.util.Collections;
1518
import java.util.List;
19+
import java.util.Map;
1620

1721
@AutoService(InstrumentationModule.class)
18-
public class RmiContextPropagationInstrumentationModule extends InstrumentationModule {
22+
public class RmiContextPropagationInstrumentationModule extends InstrumentationModule
23+
implements ExperimentalInstrumentationModule {
1924
public RmiContextPropagationInstrumentationModule() {
2025
super("rmi", "rmi-context-propagation");
2126
}
@@ -24,4 +29,11 @@ public RmiContextPropagationInstrumentationModule() {
2429
public List<TypeInstrumentation> typeInstrumentations() {
2530
return asList(new RmiClientContextInstrumentation(), new RmiServerContextInstrumentation());
2631
}
32+
33+
@Override
34+
public Map<String, List<String>> jpmsModulesToOpen() {
35+
String witnessClass = "sun.rmi.transport.StreamRemoteCall";
36+
return Collections.singletonMap(
37+
witnessClass, Arrays.asList("sun.rmi.server", "sun.rmi.transport"));
38+
}
2739
}

javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/internal/ExperimentalInstrumentationModule.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.opentelemetry.javaagent.extension.instrumentation.internal.injection.ClassInjector;
1212
import java.util.Collections;
1313
import java.util.List;
14+
import java.util.Map;
1415

1516
/**
1617
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
@@ -61,4 +62,18 @@ default String getModuleGroup() {
6162
default List<String> agentPackagesToHide() {
6263
return Collections.emptyList();
6364
}
65+
66+
/**
67+
* Some instrumentation need to access JPMS modules that are not accessible by default, this
68+
* method provides a way to access those classes like the "--add-opens" JVM command. <br>
69+
* Map key is the name of a "witness class" belonging to the module that will be loaded and used
70+
* to get a reference to the module. <br>
71+
* Map value is a list of packages to open in the module
72+
*
73+
* @return map of "witness class" name as key, list of packages as value.
74+
*/
75+
// TODO: copy the javadoc of the original implementation
76+
default Map<String, List<String>> jpmsModulesToOpen() {
77+
return Collections.emptyMap();
78+
}
6479
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.tooling;
7+
8+
import static java.util.logging.Level.FINE;
9+
import static java.util.logging.Level.WARNING;
10+
11+
import java.lang.instrument.Instrumentation;
12+
import java.util.Collection;
13+
import java.util.Collections;
14+
import java.util.HashMap;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.Set;
18+
import java.util.logging.Logger;
19+
20+
public class ModuleOpener {
21+
22+
private static final Logger logger = Logger.getLogger(ModuleOpener.class.getName());
23+
24+
private ModuleOpener() {}
25+
26+
/**
27+
* Opens JPMS module to a class loader unnamed module
28+
*
29+
* @param classFromTargetModule class from target module
30+
* @param openTo class loader to open module for
31+
* @param packagesToOpen packages to open
32+
*/
33+
public static void open(
34+
Instrumentation instrumentation,
35+
Class<?> classFromTargetModule,
36+
ClassLoader openTo,
37+
Collection<String> packagesToOpen) {
38+
39+
Module targetModule = classFromTargetModule.getModule();
40+
Module openToModule = openTo.getUnnamedModule();
41+
Set<Module> openToModuleSet = Collections.singleton(openToModule);
42+
Map<String, Set<Module>> missingOpens = new HashMap<>();
43+
for (String packageName : packagesToOpen) {
44+
if (!targetModule.isOpen(packageName, openToModule)) {
45+
missingOpens.put(packageName, openToModuleSet);
46+
logger.log(
47+
FINE,
48+
() ->
49+
String.format(
50+
"Exposing package '%s' in module '%s' to module '%s'",
51+
packageName, targetModule, openToModule));
52+
}
53+
}
54+
if (missingOpens.isEmpty()) {
55+
return;
56+
}
57+
58+
if (!instrumentation.isModifiableModule(targetModule)) {
59+
logger.log(WARNING, "Module '{}' can't be modified", targetModule);
60+
return;
61+
}
62+
63+
try {
64+
instrumentation.redefineModule(
65+
targetModule,
66+
Collections.<Module>emptySet(), // reads
67+
Collections.<String, Set<Module>>emptyMap(), // exports
68+
missingOpens, // opens
69+
Collections.<Class<?>>emptySet(), // uses
70+
Collections.<Class<?>, List<Class<?>>>emptyMap() // provides
71+
);
72+
} catch (Exception e) {
73+
logger.log(WARNING, "unable to redefine module", e);
74+
}
75+
}
76+
}

javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/IndyModuleRegistry.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55

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

8+
import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder;
89
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
910
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
11+
import io.opentelemetry.javaagent.tooling.ModuleOpener;
1012
import io.opentelemetry.javaagent.tooling.util.ClassLoaderValue;
13+
import java.lang.instrument.Instrumentation;
1114
import java.util.Map;
1215
import java.util.concurrent.ConcurrentHashMap;
1316
import net.bytebuddy.agent.builder.AgentBuilder;
@@ -65,6 +68,40 @@ public static InstrumentationModuleClassLoader getInstrumentationClassLoader(
6568
+ " yet");
6669
}
6770

71+
if (module instanceof ExperimentalInstrumentationModule) {
72+
ExperimentalInstrumentationModule experimentalModule =
73+
(ExperimentalInstrumentationModule) module;
74+
75+
// Opening JPMS modules requires to use a 'witness class' in the target module to get a
76+
// reference to the module, which means we have to eagerly load the class.
77+
//
78+
// However, this code here triggered when the advice is being executed for the first time so
79+
// this only creates a very small eager loading that is unlikely to have impact on the
80+
// application.
81+
//
82+
// Also, using a class that is already loaded like the one that is being instrumented or a
83+
// related one would increase the likeliness of not having an effect on application class
84+
// loading.
85+
86+
Instrumentation instrumentation = InstrumentationHolder.getInstrumentation();
87+
if (instrumentation == null) {
88+
throw new IllegalStateException("global instrumentation not available");
89+
}
90+
91+
experimentalModule
92+
.jpmsModulesToOpen()
93+
.forEach(
94+
(className, packages) -> {
95+
Class<?> type;
96+
try {
97+
type = Class.forName(className, false, instrumentedClassLoader);
98+
} catch (ClassNotFoundException e) {
99+
throw new IllegalStateException("missing witness class " + className, e);
100+
}
101+
102+
ModuleOpener.open(instrumentation, type, loader, packages);
103+
});
104+
}
68105
return loader;
69106
}
70107

0 commit comments

Comments
 (0)