Skip to content

Commit 16a53f2

Browse files
authored
Add Jakarta WebService Instrumentation (#7854)
* formatting * Rename method jax -> jakarta, remove unecessary enable config
1 parent 43fd5f0 commit 16a53f2

File tree

8 files changed

+313
-0
lines changed

8 files changed

+313
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
muzzle {
3+
pass {
4+
group = "jakarta.jws"
5+
module = "jakarta.jws-api"
6+
versions = "[3.0.0,]"
7+
}
8+
}
9+
10+
apply from: "$rootDir/gradle/java.gradle"
11+
12+
addTestSuiteForDir('latestDepTest', 'test')
13+
14+
dependencies {
15+
// todo correct version non rs
16+
compileOnly group: 'jakarta.jws', name: 'jakarta.jws-api', version: '3.0.0'
17+
18+
//todo also make jakarta
19+
testImplementation group: 'jakarta.jws', name: 'jakarta.jws-api', version: '3.0.0'
20+
latestDepTestImplementation group: 'jakarta.jws', name: 'jakarta.jws-api', version: '+'
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package datadog.trace.instrumentation.jakartaws;
2+
3+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
4+
import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes;
5+
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
6+
import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator;
7+
8+
public class WebServiceDecorator extends BaseDecorator {
9+
public static final WebServiceDecorator DECORATE = new WebServiceDecorator();
10+
11+
public static final CharSequence JAKARTA_WS_REQUEST =
12+
UTF8BytesString.create("jakarta-ws.request");
13+
public static final CharSequence JAKARTA_WS_ENDPOINT =
14+
UTF8BytesString.create("jakarta-ws-endpoint");
15+
16+
private WebServiceDecorator() {}
17+
18+
@Override
19+
protected String[] instrumentationNames() {
20+
return new String[] {"jakarta-ws"};
21+
}
22+
23+
@Override
24+
protected CharSequence spanType() {
25+
return InternalSpanTypes.SOAP;
26+
}
27+
28+
@Override
29+
protected CharSequence component() {
30+
return JAKARTA_WS_ENDPOINT;
31+
}
32+
33+
public void onJakartaWsSpan(final AgentSpan span, final Class<?> target, final String method) {
34+
span.setResourceName(spanNameForMethod(target, method));
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package datadog.trace.instrumentation.jakartaws;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresAnnotation;
4+
import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.hasSuperMethod;
5+
import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.hasSuperType;
6+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
7+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
8+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
9+
import static datadog.trace.instrumentation.jakartaws.WebServiceDecorator.DECORATE;
10+
import static datadog.trace.instrumentation.jakartaws.WebServiceDecorator.JAKARTA_WS_REQUEST;
11+
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
12+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
13+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
14+
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
15+
import static net.bytebuddy.matcher.ElementMatchers.not;
16+
17+
import com.google.auto.service.AutoService;
18+
import datadog.trace.agent.tooling.Instrumenter;
19+
import datadog.trace.agent.tooling.InstrumenterModule;
20+
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
21+
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
22+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
23+
import jakarta.jws.WebService;
24+
import net.bytebuddy.asm.Advice;
25+
import net.bytebuddy.description.type.TypeDescription;
26+
import net.bytebuddy.matcher.ElementMatcher;
27+
28+
@AutoService(InstrumenterModule.class)
29+
public final class WebServiceInstrumentation extends InstrumenterModule.Tracing
30+
implements Instrumenter.ForBootstrap, Instrumenter.ForTypeHierarchy {
31+
private static final String WEB_SERVICE_ANNOTATION_NAME = "jakarta.jws.WebService";
32+
33+
public WebServiceInstrumentation() {
34+
super("jakarta-ws");
35+
}
36+
37+
@Override
38+
public String hierarchyMarkerType() {
39+
return null; // bootstrap type
40+
}
41+
42+
@Override
43+
public ElementMatcher<TypeDescription> hierarchyMatcher() {
44+
return hasSuperType(declaresAnnotation(named(WEB_SERVICE_ANNOTATION_NAME)));
45+
}
46+
47+
@Override
48+
public String[] helperClassNames() {
49+
return new String[] {
50+
packageName + ".WebServiceDecorator",
51+
};
52+
}
53+
54+
@Override
55+
public void methodAdvice(MethodTransformer transformer) {
56+
transformer.applyAdvice(
57+
isMethod()
58+
.and(isPublic())
59+
.and(not(isStatic()))
60+
.and(
61+
hasSuperMethod(
62+
isDeclaredBy(declaresAnnotation(named(WEB_SERVICE_ANNOTATION_NAME))))),
63+
getClass().getName() + "$InvokeAdvice");
64+
}
65+
66+
public static final class InvokeAdvice {
67+
68+
@Advice.OnMethodEnter(suppress = Throwable.class)
69+
public static AgentScope beginRequest(
70+
@Advice.This Object thiz, @Advice.Origin("#m") String method) {
71+
final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(WebService.class);
72+
if (callDepth > 0) {
73+
return null;
74+
}
75+
76+
AgentSpan span = startSpan(JAKARTA_WS_REQUEST);
77+
span.setMeasured(true);
78+
DECORATE.onJakartaWsSpan(span, thiz.getClass(), method);
79+
DECORATE.afterStart(span);
80+
return activateSpan(span);
81+
}
82+
83+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
84+
public static void finishRequest(
85+
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable error) {
86+
if (null == scope) {
87+
return;
88+
}
89+
90+
CallDepthThreadLocalMap.reset(WebService.class);
91+
92+
AgentSpan span = scope.span();
93+
if (null != error) {
94+
DECORATE.onError(span, error);
95+
}
96+
DECORATE.beforeFinish(span);
97+
scope.close();
98+
span.finish();
99+
}
100+
}
101+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import datadog.trace.agent.test.AgentTestRunner
2+
import datadog.trace.api.DDSpanTypes
3+
4+
import static org.junit.Assert.fail
5+
6+
class WebServiceTest extends AgentTestRunner {
7+
8+
def "test successful interface request is traced"() {
9+
when:
10+
new TestService1Impl().send("success")
11+
12+
then:
13+
assertTraces(1) {
14+
trace(1) {
15+
span {
16+
operationName "jakarta-ws.request"
17+
resourceName "TestService1Impl.send"
18+
spanType DDSpanTypes.SOAP
19+
errored false
20+
parent()
21+
tags {
22+
"component" "jakarta-ws-endpoint"
23+
defaultTags()
24+
}
25+
}
26+
}
27+
}
28+
}
29+
30+
def "test successful class request is traced"() {
31+
when:
32+
new TestService2().send("success")
33+
34+
then:
35+
assertTraces(1) {
36+
trace(1) {
37+
span {
38+
operationName "jakarta-ws.request"
39+
resourceName "TestService2.send"
40+
spanType DDSpanTypes.SOAP
41+
errored false
42+
parent()
43+
tags {
44+
"component" "jakarta-ws-endpoint"
45+
defaultTags()
46+
}
47+
}
48+
}
49+
}
50+
}
51+
52+
def "test failing interface request is traced"() {
53+
when:
54+
try {
55+
new TestService1Impl().send("fail")
56+
fail("expected exception")
57+
} catch (IllegalArgumentException e) {
58+
// expected
59+
}
60+
61+
then:
62+
assertTraces(1) {
63+
trace(1) {
64+
span {
65+
operationName "jakarta-ws.request"
66+
resourceName "TestService1Impl.send"
67+
spanType DDSpanTypes.SOAP
68+
errored true
69+
parent()
70+
tags {
71+
"component" "jakarta-ws-endpoint"
72+
"error.message" "bad request"
73+
"error.type" IllegalArgumentException.name
74+
"error.stack" String
75+
defaultTags()
76+
}
77+
}
78+
}
79+
}
80+
}
81+
82+
def "test failing class request is traced"() {
83+
when:
84+
try {
85+
new TestService2().send("fail")
86+
fail("expected exception")
87+
} catch (IllegalArgumentException e) {
88+
// expected
89+
}
90+
91+
then:
92+
assertTraces(1) {
93+
trace(1) {
94+
span {
95+
operationName "jakarta-ws.request"
96+
resourceName "TestService2.send"
97+
spanType DDSpanTypes.SOAP
98+
errored true
99+
parent()
100+
tags {
101+
"component" "jakarta-ws-endpoint"
102+
"error.message" "bad request"
103+
"error.type" IllegalArgumentException.name
104+
"error.stack" String
105+
defaultTags()
106+
}
107+
}
108+
}
109+
}
110+
}
111+
112+
def "test other methods are not traced"() {
113+
when:
114+
new TestService1Impl().random()
115+
new TestService2().random()
116+
117+
then:
118+
assertTraces(0) {}
119+
}
120+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import jakarta.jws.WebService;
2+
3+
@WebService
4+
public interface TestService1 {
5+
String send(String message);
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
public class TestService1Impl implements TestService1 {
2+
@Override
3+
public String send(final String request) {
4+
if ("fail".equals(request)) {
5+
throw new IllegalArgumentException("bad request");
6+
}
7+
return random();
8+
}
9+
10+
public String random() {
11+
return Double.toHexString(Math.random());
12+
}
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import jakarta.jws.WebService;
2+
3+
@WebService
4+
public class TestService2 {
5+
public String send(final String request) {
6+
if ("fail".equals(request)) {
7+
throw new IllegalArgumentException("bad request");
8+
}
9+
return random();
10+
}
11+
12+
protected String random() {
13+
return Double.toHexString(Math.random());
14+
}
15+
}

settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ include ':dd-java-agent:instrumentation:jackson-core:jackson-core-2.16'
270270
include ':dd-java-agent:instrumentation:jacoco'
271271
include ':dd-java-agent:instrumentation:jakarta-jms'
272272
include ':dd-java-agent:instrumentation:jakarta-rs-annotations-3'
273+
include ':dd-java-agent:instrumentation:jakarta-ws-annotations'
273274
include ':dd-java-agent:instrumentation:java-concurrent'
274275
include ':dd-java-agent:instrumentation:java-concurrent:java-completablefuture'
275276
include ':dd-java-agent:instrumentation:java-concurrent:java-concurrent-21'

0 commit comments

Comments
 (0)