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,21 @@

muzzle {
pass {
group = "jakarta.jws"
module = "jakarta.jws-api"
versions = "[3.0.0,]"
}
}

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

addTestSuiteForDir('latestDepTest', 'test')

dependencies {
// todo correct version non rs
compileOnly group: 'jakarta.jws', name: 'jakarta.jws-api', version: '3.0.0'

//todo also make jakarta
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to remove comment

testImplementation group: 'jakarta.jws', name: 'jakarta.jws-api', version: '3.0.0'
latestDepTestImplementation group: 'jakarta.jws', name: 'jakarta.jws-api', version: '+'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package datadog.trace.instrumentation.jakartaws;

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 JAKARTA_WS_REQUEST =
UTF8BytesString.create("jakarta-ws.request");
public static final CharSequence JAKARTA_WS_ENDPOINT =
UTF8BytesString.create("jakarta-ws-endpoint");

private WebServiceDecorator() {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Quality Violation

Consider adding super() or this() to your constructor (...read more)

In Java, it is suggested to call super() in an extended class. This rule will report a violation if both a call to super() and an overloaded constructor are absent.

View in Datadog  Leave us feedback  Documentation


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

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

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

public void onJakartaWsSpan(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,101 @@
package datadog.trace.instrumentation.jakartaws;

import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresAnnotation;
import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.hasSuperMethod;
import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.hasSuperType;
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.jakartaws.WebServiceDecorator.DECORATE;
import static datadog.trace.instrumentation.jakartaws.WebServiceDecorator.JAKARTA_WS_REQUEST;
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.agent.tooling.InstrumenterModule;
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import jakarta.jws.WebService;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

@AutoService(InstrumenterModule.class)
public final class WebServiceInstrumentation extends InstrumenterModule.Tracing
implements Instrumenter.ForBootstrap, Instrumenter.ForTypeHierarchy {
private static final String WEB_SERVICE_ANNOTATION_NAME = "jakarta.jws.WebService";

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

@Override
public String hierarchyMarkerType() {
return null; // bootstrap type
}

@Override
public ElementMatcher<TypeDescription> hierarchyMatcher() {
return hasSuperType(declaresAnnotation(named(WEB_SERVICE_ANNOTATION_NAME)));
}

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

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
isMethod()
.and(isPublic())
.and(not(isStatic()))
.and(
hasSuperMethod(
isDeclaredBy(declaresAnnotation(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(JAKARTA_WS_REQUEST);
span.setMeasured(true);
DECORATE.onJakartaWsSpan(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,120 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.api.DDSpanTypes

import static org.junit.Assert.fail

class WebServiceTest extends AgentTestRunner {

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

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

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

then:
assertTraces(1) {
trace(1) {
span {
operationName "jakarta-ws.request"
resourceName "TestService2.send"
spanType DDSpanTypes.SOAP
errored false
parent()
tags {
"component" "jakarta-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 "jakarta-ws.request"
resourceName "TestService1Impl.send"
spanType DDSpanTypes.SOAP
errored true
parent()
tags {
"component" "jakarta-ws-endpoint"
"error.message" "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 "jakarta-ws.request"
resourceName "TestService2.send"
spanType DDSpanTypes.SOAP
errored true
parent()
tags {
"component" "jakarta-ws-endpoint"
"error.message" "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 jakarta.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 jakarta.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());
}
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ include ':dd-java-agent:instrumentation:jackson-core:jackson-core-2.16'
include ':dd-java-agent:instrumentation:jacoco'
include ':dd-java-agent:instrumentation:jakarta-jms'
include ':dd-java-agent:instrumentation:jakarta-rs-annotations-3'
include ':dd-java-agent:instrumentation:jakarta-ws-annotations'
include ':dd-java-agent:instrumentation:java-concurrent'
include ':dd-java-agent:instrumentation:java-concurrent:java-completablefuture'
include ':dd-java-agent:instrumentation:java-concurrent:java-concurrent-21'
Expand Down