Skip to content

Commit 02fe1e0

Browse files
committed
JAX-WS WebService and WebServiceProvider instrumentation
1 parent 3004acf commit 02fe1e0

File tree

13 files changed

+547
-0
lines changed

13 files changed

+547
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
muzzle {
2+
pass {
3+
group = "javax.jws"
4+
module = "javax.jws-api"
5+
versions = "[1.1,)"
6+
}
7+
}
8+
9+
apply from: "$rootDir/gradle/java.gradle"
10+
11+
dependencies {
12+
compileOnly group: 'javax.jws', name: 'javax.jws-api', version: '1.1'
13+
14+
testCompile group: 'javax.jws', name: 'javax.jws-api', version: '1.1'
15+
}
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package datadog.trace.instrumentation.jaxws1;
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 String JAX_WS_REQUEST = "jax-ws.request";
12+
public static final CharSequence JAX_WS_ENDPOINT = UTF8BytesString.create("jax-ws-endpoint");
13+
14+
private WebServiceDecorator() {}
15+
16+
@Override
17+
protected String[] instrumentationNames() {
18+
return new String[] {"jax-ws"};
19+
}
20+
21+
@Override
22+
protected CharSequence spanType() {
23+
return InternalSpanTypes.SOAP;
24+
}
25+
26+
@Override
27+
protected CharSequence component() {
28+
return JAX_WS_ENDPOINT;
29+
}
30+
31+
public void onJaxWsSpan(final AgentSpan span, final Class<?> target, final String method) {
32+
span.setResourceName(spanNameForMethod(target, method));
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package datadog.trace.instrumentation.jaxws1;
2+
3+
import static datadog.trace.agent.tooling.ClassLoaderMatcher.hasClassesNamed;
4+
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasSuperMethod;
5+
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType;
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.jaxws1.WebServiceDecorator.DECORATE;
10+
import static datadog.trace.instrumentation.jaxws1.WebServiceDecorator.JAX_WS_REQUEST;
11+
import static java.util.Collections.singletonMap;
12+
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
13+
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
14+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
15+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
16+
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
17+
import static net.bytebuddy.matcher.ElementMatchers.not;
18+
19+
import com.google.auto.service.AutoService;
20+
import datadog.trace.agent.tooling.Instrumenter;
21+
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
22+
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
23+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
24+
import java.util.Map;
25+
import javax.jws.WebService;
26+
import net.bytebuddy.asm.Advice;
27+
import net.bytebuddy.description.method.MethodDescription;
28+
import net.bytebuddy.description.type.TypeDescription;
29+
import net.bytebuddy.matcher.ElementMatcher;
30+
31+
@AutoService(Instrumenter.class)
32+
public final class WebServiceInstrumentation extends Instrumenter.Tracing {
33+
private static final String WEB_SERVICE_ANNOTATION_NAME = "javax.jws.WebService";
34+
35+
public WebServiceInstrumentation() {
36+
super("jax-ws");
37+
}
38+
39+
@Override
40+
public ElementMatcher<ClassLoader> classLoaderMatcher() {
41+
return hasClassesNamed(WEB_SERVICE_ANNOTATION_NAME);
42+
}
43+
44+
@Override
45+
public ElementMatcher<? super TypeDescription> typeMatcher() {
46+
return safeHasSuperType(isAnnotatedWith(named(WEB_SERVICE_ANNOTATION_NAME)));
47+
}
48+
49+
@Override
50+
public String[] helperClassNames() {
51+
return new String[] {
52+
packageName + ".WebServiceDecorator",
53+
};
54+
}
55+
56+
@Override
57+
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
58+
return singletonMap(
59+
isMethod()
60+
.and(isPublic())
61+
.and(not(isStatic()))
62+
.and(hasSuperMethod(isDeclaredBy(isAnnotatedWith(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(JAX_WS_REQUEST);
77+
span.setMeasured(true);
78+
DECORATE.onJaxWsSpan(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 "jax-ws.request"
17+
resourceName "TestService1Impl.send"
18+
spanType DDSpanTypes.SOAP
19+
errored false
20+
parent()
21+
tags {
22+
"component" "jax-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 "jax-ws.request"
39+
resourceName "TestService2.send"
40+
spanType DDSpanTypes.SOAP
41+
errored false
42+
parent()
43+
tags {
44+
"component" "jax-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 "jax-ws.request"
66+
resourceName "TestService1Impl.send"
67+
spanType DDSpanTypes.SOAP
68+
errored true
69+
parent()
70+
tags {
71+
"component" "jax-ws-endpoint"
72+
"error.msg" "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 "jax-ws.request"
96+
resourceName "TestService2.send"
97+
spanType DDSpanTypes.SOAP
98+
errored true
99+
parent()
100+
tags {
101+
"component" "jax-ws-endpoint"
102+
"error.msg" "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 javax.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 javax.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+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
muzzle {
2+
pass {
3+
group = "javax.xml.ws"
4+
module = "jaxws-api"
5+
versions = "[2.0,)"
6+
skipVersions += ["2.1EA2", "2.1-1"] // bad releases
7+
}
8+
}
9+
10+
apply from: "$rootDir/gradle/java.gradle"
11+
12+
apply plugin: 'org.unbroken-dome.test-sets'
13+
14+
testSets {
15+
latestDepTest {
16+
dirName = 'test'
17+
}
18+
}
19+
20+
dependencies {
21+
compileOnly group: 'javax.xml.ws', name: 'jaxws-api', version: '2.0'
22+
23+
testCompile group: 'javax.xml.ws', name: 'jaxws-api', version: '2.0'
24+
latestDepTestCompile group: 'javax.xml.ws', name: 'jaxws-api', version: '+'
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package datadog.trace.instrumentation.jaxws2;
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 WebServiceProviderDecorator extends BaseDecorator {
9+
public static final WebServiceProviderDecorator DECORATE = new WebServiceProviderDecorator();
10+
11+
public static final String JAX_WS_REQUEST = "jax-ws.request";
12+
public static final CharSequence JAX_WS_ENDPOINT = UTF8BytesString.create("jax-ws-endpoint");
13+
14+
private WebServiceProviderDecorator() {}
15+
16+
@Override
17+
protected String[] instrumentationNames() {
18+
return new String[] {"jax-ws"};
19+
}
20+
21+
@Override
22+
protected CharSequence spanType() {
23+
return InternalSpanTypes.SOAP;
24+
}
25+
26+
@Override
27+
protected CharSequence component() {
28+
return JAX_WS_ENDPOINT;
29+
}
30+
31+
public void onJaxWsSpan(final AgentSpan span, final Class<?> target, final String method) {
32+
span.setResourceName(spanNameForMethod(target, method));
33+
}
34+
}

0 commit comments

Comments
 (0)