Skip to content

Commit d6fc515

Browse files
committed
add test for proxy client
1 parent 974c315 commit d6fc515

21 files changed

+352
-118
lines changed

instrumentation/jsonrpc4j-1.3/javaagent/build.gradle.kts

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ dependencies {
1717
library("com.github.briandilley.jsonrpc4j:jsonrpc4j:1.3.3")
1818

1919
testImplementation(project(":instrumentation:jsonrpc4j-1.3:testing"))
20+
21+
testImplementation("com.fasterxml.jackson.core:jackson-databind:2.13.3")
22+
23+
testImplementation("org.eclipse.jetty:jetty-server:9.4.49.v20220914")
24+
25+
testImplementation("org.eclipse.jetty:jetty-servlet:9.4.49.v20220914")
26+
27+
testImplementation("javax.portlet:portlet-api:2.0")
2028
}
2129

2230
tasks {

instrumentation/jsonrpc4j-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsonrpc4j/v1_3/JsonRpcClientInstrumentation.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
import io.opentelemetry.context.Context;
1818
import io.opentelemetry.context.Scope;
19-
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.SimpleJsonRpcRequest;
20-
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.SimpleJsonRpcResponse;
19+
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.JsonRpcClientRequest;
20+
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.JsonRpcClientResponse;
2121
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
2222
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
2323
import java.util.Map;
@@ -63,7 +63,7 @@ public static void onEnter(
6363
@Advice.Local("otelContext") Context context,
6464
@Advice.Local("otelScope") Scope scope) {
6565
Context parentContext = Context.current();
66-
SimpleJsonRpcRequest request = new SimpleJsonRpcRequest(methodName, argument);
66+
JsonRpcClientRequest request = new JsonRpcClientRequest(methodName, argument);
6767
if (!CLIENT_INSTRUMENTER.shouldStart(parentContext, request)) {
6868
return;
6969
}
@@ -88,8 +88,8 @@ public static void onExit(
8888
scope.close();
8989
CLIENT_INSTRUMENTER.end(
9090
context,
91-
new SimpleJsonRpcRequest(methodName, argument),
92-
new SimpleJsonRpcResponse(result),
91+
new JsonRpcClientRequest(methodName, argument),
92+
new JsonRpcClientResponse(result),
9393
throwable);
9494
}
9595
}

instrumentation/jsonrpc4j-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsonrpc4j/v1_3/JsonRpcProxyInstrumentation.java

+72-3
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@
66
package io.opentelemetry.javaagent.instrumentation.jsonrpc4j.v1_3;
77

88
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
9+
import static io.opentelemetry.javaagent.instrumentation.jsonrpc4j.v1_3.JsonRpcSingletons.CLIENT_INSTRUMENTER;
910
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
1011
import static net.bytebuddy.matcher.ElementMatchers.isPrivate;
1112
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
1213
import static net.bytebuddy.matcher.ElementMatchers.named;
1314

1415
import com.googlecode.jsonrpc4j.IJsonRpcClient;
16+
import io.opentelemetry.context.Context;
17+
import io.opentelemetry.context.Scope;
18+
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.JsonRpcClientRequest;
19+
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.JsonRpcClientResponse;
1520
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
1621
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
22+
import java.lang.reflect.InvocationHandler;
23+
import java.lang.reflect.Method;
24+
import java.lang.reflect.Proxy;
1725
import java.util.Map;
1826
import net.bytebuddy.asm.Advice;
1927
import net.bytebuddy.description.type.TypeDescription;
@@ -49,9 +57,70 @@ public static <T> void onExit(
4957
@Advice.Argument(3) Map<String, String> extraHeaders,
5058
@Advice.Return(readOnly = false) Object proxy) {
5159

52-
proxy =
53-
JsonRpcSingletons.instrumentCreateClientProxy(
54-
classLoader, proxyInterface, client, extraHeaders, proxy);
60+
proxy = instrumentCreateClientProxy(classLoader, proxyInterface, client, extraHeaders, proxy);
61+
}
62+
63+
private static Object proxyObjectMethods(Method method, Object proxyObject, Object[] args) {
64+
String name = method.getName();
65+
switch (name) {
66+
case "toString":
67+
return proxyObject.getClass().getName() + "@" + System.identityHashCode(proxyObject);
68+
case "hashCode":
69+
return System.identityHashCode(proxyObject);
70+
case "equals":
71+
return proxyObject == args[0];
72+
default:
73+
throw new IllegalArgumentException(
74+
method.getName() + " is not a member of java.lang.Object");
75+
}
76+
}
77+
78+
@SuppressWarnings({"unchecked"})
79+
public static <T> T instrumentCreateClientProxy(
80+
ClassLoader classLoader,
81+
Class<T> proxyInterface,
82+
IJsonRpcClient client,
83+
Map<String, String> extraHeaders,
84+
Object proxy) {
85+
86+
return (T)
87+
Proxy.newProxyInstance(
88+
classLoader,
89+
new Class<?>[] {proxyInterface},
90+
new InvocationHandler() {
91+
@Override
92+
public Object invoke(Object proxy1, Method method, Object[] args) throws Throwable {
93+
if (method.getDeclaringClass() == Object.class) {
94+
return proxyObjectMethods(method, proxy1, args);
95+
}
96+
// before invoke
97+
Context parentContext = Context.current();
98+
JsonRpcClientRequest request = new JsonRpcClientRequest(method, args);
99+
if (!CLIENT_INSTRUMENTER.shouldStart(parentContext, request)) {
100+
return method.invoke(proxy, args);
101+
}
102+
103+
Context context = CLIENT_INSTRUMENTER.start(parentContext, request);
104+
Scope scope = context.makeCurrent();
105+
try {
106+
Object result = method.invoke(proxy, args);
107+
// after invoke
108+
scope.close();
109+
CLIENT_INSTRUMENTER.end(
110+
context,
111+
new JsonRpcClientRequest(method, args),
112+
new JsonRpcClientResponse(result),
113+
null);
114+
return result;
115+
116+
} catch (Throwable t) {
117+
// after invoke
118+
scope.close();
119+
CLIENT_INSTRUMENTER.end(context, request, null, t);
120+
throw t;
121+
}
122+
}
123+
});
55124
}
56125
}
57126
}

instrumentation/jsonrpc4j-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsonrpc4j/v1_3/JsonRpcSingletons.java

+3-55
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,18 @@
55

66
package io.opentelemetry.javaagent.instrumentation.jsonrpc4j.v1_3;
77

8-
import com.googlecode.jsonrpc4j.IJsonRpcClient;
98
import com.googlecode.jsonrpc4j.InvocationListener;
109
import io.opentelemetry.api.GlobalOpenTelemetry;
11-
import io.opentelemetry.context.Context;
12-
import io.opentelemetry.context.Scope;
1310
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
11+
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.JsonRpcClientRequest;
12+
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.JsonRpcClientResponse;
1413
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.JsonRpcTelemetry;
15-
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.SimpleJsonRpcRequest;
16-
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.SimpleJsonRpcResponse;
17-
import java.lang.reflect.InvocationHandler;
18-
import java.lang.reflect.Method;
19-
import java.lang.reflect.Proxy;
20-
import java.util.Map;
2114

2215
public final class JsonRpcSingletons {
2316

2417
public static final InvocationListener SERVER_INVOCATION_LISTENER;
2518

26-
public static final Instrumenter<SimpleJsonRpcRequest, SimpleJsonRpcResponse> CLIENT_INSTRUMENTER;
19+
public static final Instrumenter<JsonRpcClientRequest, JsonRpcClientResponse> CLIENT_INSTRUMENTER;
2720

2821
static {
2922
JsonRpcTelemetry telemetry = JsonRpcTelemetry.builder(GlobalOpenTelemetry.get()).build();
@@ -33,49 +26,4 @@ public final class JsonRpcSingletons {
3326
}
3427

3528
private JsonRpcSingletons() {}
36-
37-
@SuppressWarnings({"unchecked"})
38-
public static <T> T instrumentCreateClientProxy(
39-
ClassLoader classLoader,
40-
Class<T> proxyInterface,
41-
IJsonRpcClient client,
42-
Map<String, String> extraHeaders,
43-
Object proxy) {
44-
45-
return (T)
46-
Proxy.newProxyInstance(
47-
classLoader,
48-
new Class<?>[] {proxyInterface},
49-
new InvocationHandler() {
50-
@Override
51-
public Object invoke(Object proxy1, Method method, Object[] args) throws Throwable {
52-
// before invoke
53-
Context parentContext = Context.current();
54-
SimpleJsonRpcRequest request = new SimpleJsonRpcRequest(method, args);
55-
if (!CLIENT_INSTRUMENTER.shouldStart(parentContext, request)) {
56-
return method.invoke(proxy, args);
57-
}
58-
59-
Context context = CLIENT_INSTRUMENTER.start(parentContext, request);
60-
Scope scope = context.makeCurrent();
61-
try {
62-
Object result = method.invoke(proxy, args);
63-
// after invoke
64-
scope.close();
65-
CLIENT_INSTRUMENTER.end(
66-
context,
67-
new SimpleJsonRpcRequest(method, args),
68-
new SimpleJsonRpcResponse(result),
69-
null);
70-
return result;
71-
72-
} catch (Throwable t) {
73-
// after invoke
74-
scope.close();
75-
CLIENT_INSTRUMENTER.end(context, request, null, t);
76-
throw t;
77-
}
78-
}
79-
});
80-
}
8129
}

instrumentation/jsonrpc4j-1.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsonrpc4j/v1_3/AgentJsonRpcTest.java

+116
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,31 @@
55

66
package io.opentelemetry.javaagent.instrumentation.jsonrpc4j.v1_3;
77

8+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
9+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
10+
import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_JSONRPC_VERSION;
11+
import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD;
12+
import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE;
13+
import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM;
14+
15+
import com.fasterxml.jackson.databind.ObjectMapper;
816
import com.googlecode.jsonrpc4j.JsonRpcBasicServer;
17+
import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
18+
import com.googlecode.jsonrpc4j.ProxyUtil;
19+
import com.googlecode.jsonrpc4j.spring.rest.JsonRpcRestClient;
20+
import io.opentelemetry.api.trace.SpanKind;
921
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.AbstractJsonRpcTest;
22+
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.CalculatorService;
23+
import io.opentelemetry.instrumentation.jsonrpc4j.v1_3.CalculatorServiceImpl;
1024
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
1125
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
26+
import java.net.MalformedURLException;
27+
import java.net.URL;
28+
import java.util.HashMap;
29+
import java.util.Map;
30+
import org.junit.jupiter.api.AfterAll;
31+
import org.junit.jupiter.api.BeforeAll;
32+
import org.junit.jupiter.api.Test;
1233
import org.junit.jupiter.api.extension.RegisterExtension;
1334

1435
class AgentJsonRpcTest extends AbstractJsonRpcTest {
@@ -25,4 +46,99 @@ protected InstrumentationExtension testing() {
2546
protected JsonRpcBasicServer configureServer(JsonRpcBasicServer server) {
2647
return server;
2748
}
49+
50+
@Test
51+
void testClient() throws Throwable {
52+
CalculatorService clientProxy =
53+
ProxyUtil.createClientProxy(
54+
this.getClass().getClassLoader(), CalculatorService.class, getHttpClient());
55+
int res =
56+
testing()
57+
.runWithSpan(
58+
"parent",
59+
() -> {
60+
return clientProxy.add(1, 2);
61+
});
62+
63+
assertThat(res).isEqualTo(3);
64+
65+
testing()
66+
.waitAndAssertTraces(
67+
trace ->
68+
trace.hasSpansSatisfyingExactly(
69+
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
70+
span ->
71+
span.hasName(
72+
"io.opentelemetry.instrumentation.jsonrpc4j.v1_3.CalculatorService/add")
73+
.hasKind(SpanKind.CLIENT)
74+
.hasParent(trace.getSpan(0))
75+
.hasAttributesSatisfyingExactly(
76+
equalTo(RPC_SYSTEM, "jsonrpc"),
77+
equalTo(RPC_JSONRPC_VERSION, "2.0"),
78+
equalTo(
79+
RPC_SERVICE,
80+
"io.opentelemetry.instrumentation.jsonrpc4j.v1_3.CalculatorService"),
81+
equalTo(RPC_METHOD, "add"))),
82+
trace -> trace.hasSpansSatisfyingExactly(span -> span.hasKind(SpanKind.SERVER)));
83+
84+
testing()
85+
.waitAndAssertMetrics(
86+
"io.opentelemetry.jsonrpc4j-1.3",
87+
"rpc.client.duration",
88+
metrics ->
89+
metrics.anySatisfy(
90+
metric ->
91+
assertThat(metric)
92+
.hasUnit("ms")
93+
.hasHistogramSatisfying(
94+
histogram ->
95+
histogram.hasPointsSatisfying(
96+
point ->
97+
point.hasAttributesSatisfying(
98+
equalTo(RPC_METHOD, "add"),
99+
equalTo(
100+
RPC_SERVICE,
101+
"io.opentelemetry.instrumentation.jsonrpc4j.v1_3.CalculatorService"),
102+
equalTo(RPC_SYSTEM, "jsonrpc"))))));
103+
}
104+
105+
private JettyServer jettyServer;
106+
107+
@BeforeAll
108+
public void setup() throws Exception {
109+
this.jettyServer = createServer();
110+
}
111+
112+
private static JettyServer createServer() throws Exception {
113+
JettyServer jettyServer = new JettyServer(CalculatorServiceImpl.class);
114+
jettyServer.startup();
115+
return jettyServer;
116+
}
117+
118+
protected JsonRpcRestClient getClient() throws MalformedURLException {
119+
return getClient(JettyServer.SERVLET);
120+
}
121+
122+
protected JsonRpcRestClient getClient(String servlet) throws MalformedURLException {
123+
return new JsonRpcRestClient(new URL(jettyServer.getCustomServerUrlString(servlet)));
124+
}
125+
126+
protected JsonRpcHttpClient getHttpClient() throws MalformedURLException {
127+
Map<String, String> header = new HashMap<>();
128+
return new JsonRpcHttpClient(
129+
new ObjectMapper(),
130+
new URL(jettyServer.getCustomServerUrlString(JettyServer.SERVLET)),
131+
header);
132+
}
133+
134+
protected JsonRpcHttpClient getHttpClient(String servlet) throws MalformedURLException {
135+
Map<String, String> header = new HashMap<>();
136+
return new JsonRpcHttpClient(
137+
new ObjectMapper(), new URL(jettyServer.getCustomServerUrlString(servlet)), header);
138+
}
139+
140+
@AfterAll
141+
public void teardown() throws Exception {
142+
jettyServer.stop();
143+
}
28144
}

0 commit comments

Comments
 (0)