Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
muzzle {
pass {
group = "javax.jws"
module = "javax.jws-api"
versions = "[1.1,)"
}
}

apply from: "$rootDir/gradle/java.gradle"

dependencies {
compileOnly group: 'javax.jws', name: 'javax.jws-api', version: '1.1'

testCompile group: 'javax.jws', name: 'javax.jws-api', version: '1.1'
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package datadog.trace.instrumentation.jaxws1;

import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes;
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator;

public class WebServiceDecorator extends BaseDecorator {
public static final WebServiceDecorator DECORATE = new WebServiceDecorator();

public static final CharSequence JAX_WS_REQUEST = UTF8BytesString.create("jax-ws.request");
public static final CharSequence JAX_WS_ENDPOINT = UTF8BytesString.create("jax-ws-endpoint");

private WebServiceDecorator() {}

@Override
protected String[] instrumentationNames() {
return new String[] {"jax-ws"};
}

@Override
protected CharSequence spanType() {
return InternalSpanTypes.SOAP;
}

@Override
protected CharSequence component() {
return JAX_WS_ENDPOINT;
}

public void onJaxWsSpan(final AgentSpan span, final Class<?> target, final String method) {
span.setResourceName(spanNameForMethod(target, method));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package datadog.trace.instrumentation.jaxws1;

import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasSuperMethod;
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType;
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.jaxws1.WebServiceDecorator.DECORATE;
import static datadog.trace.instrumentation.jaxws1.WebServiceDecorator.JAX_WS_REQUEST;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.not;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import java.util.Map;
import javax.jws.WebService;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

@AutoService(Instrumenter.class)
public final class WebServiceInstrumentation extends Instrumenter.Tracing {
private static final String WEB_SERVICE_ANNOTATION_NAME = "javax.jws.WebService";

public WebServiceInstrumentation() {
super("jax-ws");
}

@Override
protected boolean defaultEnabled() {
return false;
}

@Override
public ElementMatcher<? super TypeDescription> typeMatcher() {
return safeHasSuperType(isAnnotatedWith(named(WEB_SERVICE_ANNOTATION_NAME)));
}

@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".WebServiceDecorator",
};
}

@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
isMethod()
.and(isPublic())
.and(not(isStatic()))
.and(hasSuperMethod(isDeclaredBy(isAnnotatedWith(named(WEB_SERVICE_ANNOTATION_NAME))))),
getClass().getName() + "$InvokeAdvice");
}

public static final class InvokeAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static AgentScope beginRequest(
@Advice.This Object thiz, @Advice.Origin("#m") String method) {
final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(WebService.class);
if (callDepth > 0) {
return null;
}

AgentSpan span = startSpan(JAX_WS_REQUEST);
span.setMeasured(true);
DECORATE.onJaxWsSpan(span, thiz.getClass(), method);
DECORATE.afterStart(span);
return activateSpan(span);
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void finishRequest(
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable error) {
if (null == scope) {
return;
}

CallDepthThreadLocalMap.reset(WebService.class);

AgentSpan span = scope.span();
if (null != error) {
DECORATE.onError(span, error);
}
DECORATE.beforeFinish(span);
scope.close();
span.finish();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.api.DDSpanTypes

import static org.junit.Assert.fail

class WebServiceTest extends AgentTestRunner {

@Override
void configurePreAgent() {
super.configurePreAgent()
injectSysConfig("dd.integration.jax-ws.enabled", "true")
}

def "test successful interface request is traced"() {
when:
new TestService1Impl().send("success")

then:
assertTraces(1) {
trace(1) {
span {
operationName "jax-ws.request"
resourceName "TestService1Impl.send"
spanType DDSpanTypes.SOAP
errored false
parent()
tags {
"component" "jax-ws-endpoint"
defaultTags()
}
}
}
}
}

def "test successful class request is traced"() {
when:
new TestService2().send("success")

then:
assertTraces(1) {
trace(1) {
span {
operationName "jax-ws.request"
resourceName "TestService2.send"
spanType DDSpanTypes.SOAP
errored false
parent()
tags {
"component" "jax-ws-endpoint"
defaultTags()
}
}
}
}
}

def "test failing interface request is traced"() {
when:
try {
new TestService1Impl().send("fail")
fail("expected exception")
} catch (IllegalArgumentException e) {
// expected
}

then:
assertTraces(1) {
trace(1) {
span {
operationName "jax-ws.request"
resourceName "TestService1Impl.send"
spanType DDSpanTypes.SOAP
errored true
parent()
tags {
"component" "jax-ws-endpoint"
"error.msg" "bad request"
"error.type" IllegalArgumentException.name
"error.stack" String
defaultTags()
}
}
}
}
}

def "test failing class request is traced"() {
when:
try {
new TestService2().send("fail")
fail("expected exception")
} catch (IllegalArgumentException e) {
// expected
}

then:
assertTraces(1) {
trace(1) {
span {
operationName "jax-ws.request"
resourceName "TestService2.send"
spanType DDSpanTypes.SOAP
errored true
parent()
tags {
"component" "jax-ws-endpoint"
"error.msg" "bad request"
"error.type" IllegalArgumentException.name
"error.stack" String
defaultTags()
}
}
}
}
}

def "test other methods are not traced"() {
when:
new TestService1Impl().random()
new TestService2().random()

then:
assertTraces(0) {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import javax.jws.WebService;

@WebService
public interface TestService1 {
String send(String message);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
public class TestService1Impl implements TestService1 {
@Override
public String send(final String request) {
if ("fail".equals(request)) {
throw new IllegalArgumentException("bad request");
}
return random();
}

public String random() {
return Double.toHexString(Math.random());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import javax.jws.WebService;

@WebService
public class TestService2 {
public String send(final String request) {
if ("fail".equals(request)) {
throw new IllegalArgumentException("bad request");
}
return random();
}

protected String random() {
return Double.toHexString(Math.random());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
muzzle {
pass {
group = "javax.xml.ws"
module = "jaxws-api"
versions = "[2.0,)"
skipVersions += ["2.1EA2", "2.1-1"] // bad releases
}
}

apply from: "$rootDir/gradle/java.gradle"

apply plugin: 'org.unbroken-dome.test-sets'

testSets {
latestDepTest {
dirName = 'test'
}
}

dependencies {
compileOnly group: 'javax.xml.ws', name: 'jaxws-api', version: '2.0'

testCompile group: 'javax.xml.ws', name: 'jaxws-api', version: '2.0'
latestDepTestCompile group: 'javax.xml.ws', name: 'jaxws-api', version: '+'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package datadog.trace.instrumentation.jaxws2;

import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes;
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
import datadog.trace.bootstrap.instrumentation.decorator.BaseDecorator;

public class WebServiceProviderDecorator extends BaseDecorator {
public static final WebServiceProviderDecorator DECORATE = new WebServiceProviderDecorator();

public static final CharSequence JAX_WS_REQUEST = UTF8BytesString.create("jax-ws.request");
public static final CharSequence JAX_WS_ENDPOINT = UTF8BytesString.create("jax-ws-endpoint");

private WebServiceProviderDecorator() {}

@Override
protected String[] instrumentationNames() {
return new String[] {"jax-ws"};
}

@Override
protected CharSequence spanType() {
return InternalSpanTypes.SOAP;
}

@Override
protected CharSequence component() {
return JAX_WS_ENDPOINT;
}

public void onJaxWsSpan(final AgentSpan span, final Class<?> target, final String method) {
span.setResourceName(spanNameForMethod(target, method));
}
}
Loading