Skip to content

Commit 1bc3d70

Browse files
authored
Merge pull request #2473 from DataDog/mcculls/jax-ws
JAX-WS WebService and WebServiceProvider instrumentation (turned off by default)
2 parents e1bc8c7 + 2807be8 commit 1bc3d70

File tree

13 files changed

+557
-0
lines changed

13 files changed

+557
-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 CharSequence JAX_WS_REQUEST = UTF8BytesString.create("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,100 @@
1+
package datadog.trace.instrumentation.jaxws1;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasSuperMethod;
4+
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType;
5+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
6+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
7+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
8+
import static datadog.trace.instrumentation.jaxws1.WebServiceDecorator.DECORATE;
9+
import static datadog.trace.instrumentation.jaxws1.WebServiceDecorator.JAX_WS_REQUEST;
10+
import static java.util.Collections.singletonMap;
11+
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
12+
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
13+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
14+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
15+
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
16+
import static net.bytebuddy.matcher.ElementMatchers.not;
17+
18+
import com.google.auto.service.AutoService;
19+
import datadog.trace.agent.tooling.Instrumenter;
20+
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
21+
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
22+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
23+
import java.util.Map;
24+
import javax.jws.WebService;
25+
import net.bytebuddy.asm.Advice;
26+
import net.bytebuddy.description.method.MethodDescription;
27+
import net.bytebuddy.description.type.TypeDescription;
28+
import net.bytebuddy.matcher.ElementMatcher;
29+
30+
@AutoService(Instrumenter.class)
31+
public final class WebServiceInstrumentation extends Instrumenter.Tracing {
32+
private static final String WEB_SERVICE_ANNOTATION_NAME = "javax.jws.WebService";
33+
34+
public WebServiceInstrumentation() {
35+
super("jax-ws");
36+
}
37+
38+
@Override
39+
protected boolean defaultEnabled() {
40+
return false;
41+
}
42+
43+
@Override
44+
public ElementMatcher<? super TypeDescription> typeMatcher() {
45+
return safeHasSuperType(isAnnotatedWith(named(WEB_SERVICE_ANNOTATION_NAME)));
46+
}
47+
48+
@Override
49+
public String[] helperClassNames() {
50+
return new String[] {
51+
packageName + ".WebServiceDecorator",
52+
};
53+
}
54+
55+
@Override
56+
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
57+
return singletonMap(
58+
isMethod()
59+
.and(isPublic())
60+
.and(not(isStatic()))
61+
.and(hasSuperMethod(isDeclaredBy(isAnnotatedWith(named(WEB_SERVICE_ANNOTATION_NAME))))),
62+
getClass().getName() + "$InvokeAdvice");
63+
}
64+
65+
public static final class InvokeAdvice {
66+
67+
@Advice.OnMethodEnter(suppress = Throwable.class)
68+
public static AgentScope beginRequest(
69+
@Advice.This Object thiz, @Advice.Origin("#m") String method) {
70+
final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(WebService.class);
71+
if (callDepth > 0) {
72+
return null;
73+
}
74+
75+
AgentSpan span = startSpan(JAX_WS_REQUEST);
76+
span.setMeasured(true);
77+
DECORATE.onJaxWsSpan(span, thiz.getClass(), method);
78+
DECORATE.afterStart(span);
79+
return activateSpan(span);
80+
}
81+
82+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
83+
public static void finishRequest(
84+
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable error) {
85+
if (null == scope) {
86+
return;
87+
}
88+
89+
CallDepthThreadLocalMap.reset(WebService.class);
90+
91+
AgentSpan span = scope.span();
92+
if (null != error) {
93+
DECORATE.onError(span, error);
94+
}
95+
DECORATE.beforeFinish(span);
96+
scope.close();
97+
span.finish();
98+
}
99+
}
100+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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+
@Override
9+
void configurePreAgent() {
10+
super.configurePreAgent()
11+
injectSysConfig("dd.integration.jax-ws.enabled", "true")
12+
}
13+
14+
def "test successful interface request is traced"() {
15+
when:
16+
new TestService1Impl().send("success")
17+
18+
then:
19+
assertTraces(1) {
20+
trace(1) {
21+
span {
22+
operationName "jax-ws.request"
23+
resourceName "TestService1Impl.send"
24+
spanType DDSpanTypes.SOAP
25+
errored false
26+
parent()
27+
tags {
28+
"component" "jax-ws-endpoint"
29+
defaultTags()
30+
}
31+
}
32+
}
33+
}
34+
}
35+
36+
def "test successful class request is traced"() {
37+
when:
38+
new TestService2().send("success")
39+
40+
then:
41+
assertTraces(1) {
42+
trace(1) {
43+
span {
44+
operationName "jax-ws.request"
45+
resourceName "TestService2.send"
46+
spanType DDSpanTypes.SOAP
47+
errored false
48+
parent()
49+
tags {
50+
"component" "jax-ws-endpoint"
51+
defaultTags()
52+
}
53+
}
54+
}
55+
}
56+
}
57+
58+
def "test failing interface request is traced"() {
59+
when:
60+
try {
61+
new TestService1Impl().send("fail")
62+
fail("expected exception")
63+
} catch (IllegalArgumentException e) {
64+
// expected
65+
}
66+
67+
then:
68+
assertTraces(1) {
69+
trace(1) {
70+
span {
71+
operationName "jax-ws.request"
72+
resourceName "TestService1Impl.send"
73+
spanType DDSpanTypes.SOAP
74+
errored true
75+
parent()
76+
tags {
77+
"component" "jax-ws-endpoint"
78+
"error.msg" "bad request"
79+
"error.type" IllegalArgumentException.name
80+
"error.stack" String
81+
defaultTags()
82+
}
83+
}
84+
}
85+
}
86+
}
87+
88+
def "test failing class request is traced"() {
89+
when:
90+
try {
91+
new TestService2().send("fail")
92+
fail("expected exception")
93+
} catch (IllegalArgumentException e) {
94+
// expected
95+
}
96+
97+
then:
98+
assertTraces(1) {
99+
trace(1) {
100+
span {
101+
operationName "jax-ws.request"
102+
resourceName "TestService2.send"
103+
spanType DDSpanTypes.SOAP
104+
errored true
105+
parent()
106+
tags {
107+
"component" "jax-ws-endpoint"
108+
"error.msg" "bad request"
109+
"error.type" IllegalArgumentException.name
110+
"error.stack" String
111+
defaultTags()
112+
}
113+
}
114+
}
115+
}
116+
}
117+
118+
def "test other methods are not traced"() {
119+
when:
120+
new TestService1Impl().random()
121+
new TestService2().random()
122+
123+
then:
124+
assertTraces(0) {}
125+
}
126+
}
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 CharSequence JAX_WS_REQUEST = UTF8BytesString.create("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)