Support RESTeasy 4.0 version in unit tests (#1082)

* Support RESTeasy 4.0 version in unit tests

* Split jaxrs-2.0 into jaxrs-2.0-common and jaxrs-2.0-testing (similar to e.g. play)
* Support newest RESTEasy version in unit tests

* Apply suggestions from code review

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>

* Support RESTeasy 4.0 version in unit tests - code review follow-up

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
Mateusz Rzeszutek 2020-09-11 06:21:16 +02:00 committed by GitHub
parent 266b8b927d
commit 9566315914
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 511 additions and 264 deletions

View File

@ -4,14 +4,11 @@ ext {
apply from: "$rootDir/gradle/instrumentation.gradle"
dependencies {
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-jersey-2.0')
testImplementation project(':instrumentation:servlet:servlet-3.0')
// First version with DropwizardTestSupport:
testImplementation group: 'io.dropwizard', name: 'dropwizard-testing', version: '0.8.0'
testImplementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.2.3'
testImplementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-afterburner', version: '2.9.10'
// Anything 1.0+ fails with a java.lang.NoClassDefFoundError: org/eclipse/jetty/server/RequestLog
// latestDepTestLibrary group: 'io.dropwizard', name: 'dropwizard-testing', version: '1.+'
}

View File

@ -0,0 +1,18 @@
apply from: "$rootDir/gradle/instrumentation.gradle"
muzzle {
fail {
group = "javax.ws.rs"
module = "jsr311-api"
versions = "[,]"
}
pass {
group = "javax.ws.rs"
module = "javax.ws.rs-api"
versions = "[,]"
}
}
dependencies {
compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
}

View File

@ -19,15 +19,14 @@ package io.opentelemetry.instrumentation.auto.jaxrs.v2_0;
import static io.opentelemetry.instrumentation.auto.jaxrs.v2_0.JaxRsAnnotationsTracer.TRACER;
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.instrumentation.auto.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.Instrumenter;
import io.opentelemetry.trace.Span;
import java.lang.reflect.Method;
@ -74,39 +73,39 @@ public abstract class AbstractRequestContextInstrumentation extends Instrumenter
}
public static class RequestFilterHelper {
public static SpanWithScope createOrUpdateAbortSpan(
public static Span createOrUpdateAbortSpan(
ContainerRequestContext context, Class<?> resourceClass, Method method) {
if (method != null && resourceClass != null) {
context.setProperty(JaxRsAnnotationsTracer.ABORT_HANDLED, true);
Span parent = BaseTracer.getCurrentServerSpan();
Span span = TRACER.getCurrentSpan();
Span serverSpan = BaseTracer.getCurrentServerSpan();
Span currentSpan = TRACER.getCurrentSpan();
if (span == null) {
span = TRACER.startSpan(resourceClass, method);
return new SpanWithScope(span, currentContextWith(span));
// if there's no current span or it's the same as the server (servlet) span we need to start
// a JAX-RS one
// in other case, DefaultRequestContextInstrumentation must have already run so it's enough
// to just update the names
if (currentSpan == null || currentSpan == serverSpan) {
return TRACER.startSpan(resourceClass, method);
} else {
TRACER.updateSpanNames(span, parent, resourceClass, method);
return null;
TRACER.updateSpanNames(currentSpan, serverSpan, resourceClass, method);
}
} else {
return null;
}
return null;
}
public static void closeSpanAndScope(SpanWithScope spanWithScope, Throwable throwable) {
if (spanWithScope == null) {
public static void closeSpanAndScope(Span span, Scope scope, Throwable throwable) {
if (span == null || scope == null) {
return;
}
Span span = spanWithScope.getSpan();
if (throwable != null) {
TRACER.endExceptionally(span, throwable);
} else {
TRACER.end(span);
}
spanWithScope.closeScope();
scope.close();
}
}
}

View File

@ -17,15 +17,15 @@
package io.opentelemetry.instrumentation.auto.jaxrs.v2_0;
import static io.opentelemetry.instrumentation.auto.jaxrs.v2_0.JaxRsAnnotationsTracer.TRACER;
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.auto.api.SpanWithScope;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.tooling.Instrumenter;
import io.opentelemetry.trace.Span;
import java.lang.reflect.Method;
import javax.ws.rs.container.ContainerRequestContext;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.Local;
/**
* Default context instrumentation.
@ -40,10 +40,13 @@ import net.bytebuddy.asm.Advice;
public class DefaultRequestContextInstrumentation extends AbstractRequestContextInstrumentation {
public static class ContainerRequestContextAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope createGenericSpan(@Advice.This ContainerRequestContext context) {
public static void createGenericSpan(
@Advice.This ContainerRequestContext context,
@Local("otelSpan") Span span,
@Local("otelScope") Scope scope) {
if (context.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null) {
Class filterClass = (Class) context.getProperty(JaxRsAnnotationsTracer.ABORT_FILTER_CLASS);
Class<?> filterClass =
(Class<?>) context.getProperty(JaxRsAnnotationsTracer.ABORT_FILTER_CLASS);
Method method = null;
try {
method = filterClass.getMethod("filter", ContainerRequestContext.class);
@ -52,29 +55,17 @@ public class DefaultRequestContextInstrumentation extends AbstractRequestContext
// can only be aborted inside the filter method
}
Span span = TRACER.startSpan(filterClass, method);
return new SpanWithScope(span, currentContextWith(span));
span = TRACER.startSpan(filterClass, method);
scope = TRACER.startScope(span);
}
return null;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) {
if (spanWithScope == null) {
return;
}
Span span = spanWithScope.getSpan();
if (throwable != null) {
TRACER.endExceptionally(span, throwable);
} else {
TRACER.end(span);
}
spanWithScope.closeScope();
@Local("otelSpan") Span span,
@Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable) {
RequestFilterHelper.closeSpanAndScope(span, scope, throwable);
}
}
}

View File

@ -21,7 +21,6 @@ import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNa
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.hasSuperMethod;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.safeHasSuperType;
import static io.opentelemetry.javaagent.tooling.matcher.NameMatchers.namedOneOf;
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
@ -29,10 +28,10 @@ import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.auto.api.CallDepthThreadLocalMap;
import io.opentelemetry.instrumentation.auto.api.ContextStore;
import io.opentelemetry.instrumentation.auto.api.InstrumentationContext;
import io.opentelemetry.instrumentation.auto.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.Instrumenter;
import io.opentelemetry.trace.Span;
import java.lang.reflect.Method;
@ -101,11 +100,13 @@ public final class JaxRsAnnotationsInstrumentation extends Instrumenter.Default
public static class JaxRsAnnotationsAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope nameSpan(
public static void nameSpan(
@Advice.This Object target,
@Advice.Origin Method method,
@Advice.AllArguments Object[] args,
@Advice.Local("asyncResponse") AsyncResponse asyncResponse) {
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelAsyncResponse") AsyncResponse asyncResponse) {
ContextStore<AsyncResponse, Span> contextStore = null;
for (Object arg : args) {
if (arg instanceof AsyncResponse) {
@ -118,39 +119,39 @@ public final class JaxRsAnnotationsInstrumentation extends Instrumenter.Default
* could work around this by using a list instead, but we likely don't want the extra
* span anyway.
*/
return null;
return;
}
break;
}
}
if (CallDepthThreadLocalMap.incrementCallDepth(Path.class) > 0) {
return null;
return;
}
Span span = TRACER.startSpan(target.getClass(), method);
span = TRACER.startSpan(target.getClass(), method);
if (contextStore != null && asyncResponse != null) {
contextStore.put(asyncResponse, span);
}
return new SpanWithScope(span, currentContextWith(span));
scope = TRACER.startScope(span);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Thrown Throwable throwable,
@Advice.Local("asyncResponse") AsyncResponse asyncResponse) {
if (spanWithScope == null) {
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelAsyncResponse") AsyncResponse asyncResponse) {
if (span == null || scope == null) {
return;
}
CallDepthThreadLocalMap.reset(Path.class);
Span span = spanWithScope.getSpan();
if (throwable != null) {
TRACER.endExceptionally(span, throwable);
spanWithScope.closeScope();
scope.close();
return;
}
@ -161,7 +162,7 @@ public final class JaxRsAnnotationsInstrumentation extends Instrumenter.Default
if (asyncResponse == null || !asyncResponse.isSuspended()) {
TRACER.end(span);
}
spanWithScope.closeScope();
scope.close();
// else span finished by AsyncResponseAdvice
}
}

View File

@ -18,6 +18,7 @@ package io.opentelemetry.instrumentation.auto.jaxrs.v2_0;
import static io.opentelemetry.instrumentation.auto.api.WeakMap.Provider.newWeakMap;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.instrumentation.auto.api.WeakMap;
import io.opentelemetry.javaagent.tooling.ClassHierarchyIterable;
@ -59,6 +60,10 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
}
}
public Scope startScope(Span span) {
return tracer.withSpan(span);
}
private void updateSpanName(Span span, String spanName) {
if (!spanName.isEmpty()) {
span.updateName(spanName);

View File

@ -2,7 +2,7 @@ apply from: "$rootDir/gradle/instrumentation.gradle"
muzzle {
// Cant assert fails because muzzle assumes all instrumentations will fail
// Instrumentations in jaxrs-2.0 will pass
// Instrumentations in jaxrs-2.0-common will pass
pass {
group = "org.glassfish.jersey.core"
module = "jersey-server"
@ -14,5 +14,13 @@ dependencies {
compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
compileOnly group: 'org.glassfish.jersey.core', name: 'jersey-server', version: '2.0'
implementation project(':instrumentation:jaxrs:jaxrs-2.0')
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common')
testImplementation project(':instrumentation:servlet:servlet-3.0')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing')
// First version with DropwizardTestSupport:
testLibrary group: 'io.dropwizard', name: 'dropwizard-testing', version: '0.8.0'
testImplementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.2.3'
testImplementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-afterburner', version: '2.9.10'
}

View File

@ -16,14 +16,18 @@
package io.opentelemetry.instrumentation.auto.jaxrs.v2_0;
import static io.opentelemetry.instrumentation.auto.jaxrs.v2_0.JaxRsAnnotationsTracer.TRACER;
import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.auto.api.SpanWithScope;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.tooling.Instrumenter;
import io.opentelemetry.trace.Span;
import java.lang.reflect.Method;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.UriInfo;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.Local;
/**
* Jersey specific context instrumentation.
@ -38,7 +42,10 @@ import net.bytebuddy.asm.Advice;
public class JerseyRequestContextInstrumentation extends AbstractRequestContextInstrumentation {
public static class ContainerRequestContextAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope decorateAbortSpan(@Advice.This ContainerRequestContext context) {
public static void decorateAbortSpan(
@Advice.This ContainerRequestContext context,
@Local("otelSpan") Span span,
@Local("otelScope") Scope scope) {
UriInfo uriInfo = context.getUriInfo();
if (context.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null
@ -46,18 +53,21 @@ public class JerseyRequestContextInstrumentation extends AbstractRequestContextI
ResourceInfo resourceInfo = (ResourceInfo) uriInfo;
Method method = resourceInfo.getResourceMethod();
Class resourceClass = resourceInfo.getResourceClass();
Class<?> resourceClass = resourceInfo.getResourceClass();
return RequestFilterHelper.createOrUpdateAbortSpan(context, resourceClass, method);
span = RequestFilterHelper.createOrUpdateAbortSpan(context, resourceClass, method);
if (span != null) {
scope = TRACER.startScope(span);
}
}
return null;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter SpanWithScope scope, @Advice.Thrown Throwable throwable) {
RequestFilterHelper.closeSpanAndScope(scope, throwable);
@Local("otelSpan") Span span,
@Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable) {
RequestFilterHelper.closeSpanAndScope(span, scope, throwable);
}
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class JerseyAnnotationInstrumentationTest extends JaxRsAnnotationsInstrumentationTest {
}

View File

@ -0,0 +1,44 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import static Resource.Test1
import static Resource.Test2
import static Resource.Test3
import io.dropwizard.testing.junit.ResourceTestRule
import javax.ws.rs.client.Entity
import javax.ws.rs.core.Response
import org.junit.ClassRule
import spock.lang.Shared
class JerseyFilterTest extends JaxRsFilterTest {
@Shared
@ClassRule
ResourceTestRule resources = ResourceTestRule.builder()
.addResource(new Test1())
.addResource(new Test2())
.addResource(new Test3())
.addProvider(simpleRequestFilter)
.addProvider(prematchRequestFilter)
.build()
@Override
def makeRequest(String url) {
Response response = resources.client().target(url).request().post(Entity.text(""))
return [response.readEntity(String), response.statusInfo.statusCode]
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import io.dropwizard.jetty.NonblockingServletHolder
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.ServletContextHandler
import org.glassfish.jersey.server.ResourceConfig
import org.glassfish.jersey.servlet.ServletContainer
class JerseyHttpServerTest extends JaxRsHttpServerTest<Server> {
@Override
Server startServer(int port) {
def servlet = new ServletContainer(ResourceConfig.forApplicationClass(JaxRsTestApplication))
def handler = new ServletContextHandler(ServletContextHandler.SESSIONS)
handler.setContextPath("/")
handler.addServlet(new NonblockingServletHolder(servlet), "/*")
def server = new Server(port)
server.setHandler(handler)
server.start()
return server
}
@Override
void stopServer(Server httpServer) {
httpServer.stop()
}
}

View File

@ -2,7 +2,7 @@ apply from: "$rootDir/gradle/instrumentation.gradle"
muzzle {
// Cant assert fails because muzzle assumes all instrumentations will fail
// Instrumentations in jaxrs-2.0 will pass
// Instrumentations in jaxrs-2.0-common will pass
// Resteasy changes a class's package in 3.1.0 then moves it back in 3.5.0 and then moves it forward again in 4.0.0
// so the jaxrs-2.0-resteasy-3.0 module applies to [3.0, 3.1) and [3.5, 4.0)
@ -22,7 +22,20 @@ muzzle {
dependencies {
compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
compileOnly group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.0.0.Final'
library group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.0.0.Final'
implementation project(':instrumentation:jaxrs:jaxrs-2.0')
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common')
testImplementation project(':instrumentation:servlet:servlet-3.0')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing')
testLibrary(group: 'org.jboss.resteasy', name: 'resteasy-undertow', version: '3.0.4.Final') {
exclude group: 'org.jboss.resteasy', module: 'resteasy-client'
}
testLibrary group: 'io.undertow', name: 'undertow-servlet', version: '1.0.0.Final'
latestDepTestLibrary group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.+'
latestDepTestLibrary(group: 'org.jboss.resteasy', name: 'resteasy-undertow', version: '3.+') {
exclude group: 'org.jboss.resteasy', module: 'resteasy-client'
}
}

View File

@ -16,12 +16,16 @@
package io.opentelemetry.instrumentation.auto.jaxrs.v2_0;
import static io.opentelemetry.instrumentation.auto.jaxrs.v2_0.JaxRsAnnotationsTracer.TRACER;
import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.auto.api.SpanWithScope;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.tooling.Instrumenter;
import io.opentelemetry.trace.Span;
import java.lang.reflect.Method;
import javax.ws.rs.container.ContainerRequestContext;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.Local;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.interception.PostMatchContainerRequestContext;
@ -38,27 +42,32 @@ import org.jboss.resteasy.core.interception.PostMatchContainerRequestContext;
@AutoService(Instrumenter.class)
public class Resteasy30RequestContextInstrumentation extends AbstractRequestContextInstrumentation {
public static class ContainerRequestContextAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope decorateAbortSpan(@Advice.This ContainerRequestContext context) {
public static void decorateAbortSpan(
@Advice.This ContainerRequestContext context,
@Local("otelSpan") Span span,
@Local("otelScope") Scope scope) {
if (context.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null
&& context instanceof PostMatchContainerRequestContext) {
ResourceMethodInvoker resourceMethodInvoker =
((PostMatchContainerRequestContext) context).getResourceMethod();
Method method = resourceMethodInvoker.getMethod();
Class resourceClass = resourceMethodInvoker.getResourceClass();
Class<?> resourceClass = resourceMethodInvoker.getResourceClass();
return RequestFilterHelper.createOrUpdateAbortSpan(context, resourceClass, method);
span = RequestFilterHelper.createOrUpdateAbortSpan(context, resourceClass, method);
if (span != null) {
scope = TRACER.startScope(span);
}
}
return null;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter SpanWithScope scope, @Advice.Thrown Throwable throwable) {
RequestFilterHelper.closeSpanAndScope(scope, throwable);
@Local("otelSpan") Span span,
@Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable) {
RequestFilterHelper.closeSpanAndScope(span, scope, throwable);
}
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ResteasyAnnotationInstrumentationTest extends JaxRsAnnotationsInstrumentationTest {
}

View File

@ -0,0 +1,54 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import static Resource.Test1
import static Resource.Test2
import static Resource.Test3
import javax.ws.rs.core.MediaType
import org.jboss.resteasy.mock.MockDispatcherFactory
import org.jboss.resteasy.mock.MockHttpRequest
import org.jboss.resteasy.mock.MockHttpResponse
import spock.lang.Shared
class ResteasyFilterTest extends JaxRsFilterTest {
@Shared
def dispatcher
def setupSpec() {
dispatcher = MockDispatcherFactory.createDispatcher()
def registry = dispatcher.getRegistry()
registry.addSingletonResource(new Test1())
registry.addSingletonResource(new Test2())
registry.addSingletonResource(new Test3())
dispatcher.getProviderFactory().register(simpleRequestFilter)
dispatcher.getProviderFactory().register(prematchRequestFilter)
}
@Override
def makeRequest(String url) {
MockHttpRequest request = MockHttpRequest.post(url)
request.contentType(MediaType.TEXT_PLAIN_TYPE)
request.content(new byte[0])
MockHttpResponse response = new MockHttpResponse()
dispatcher.invoke(request, response)
return [response.contentAsString, response.status]
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import io.undertow.Undertow
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer
class ResteasyHttpServerTest extends JaxRsHttpServerTest<UndertowJaxrsServer> {
@Override
UndertowJaxrsServer startServer(int port) {
def server = new UndertowJaxrsServer()
server.deploy(JaxRsTestApplication)
server.start(Undertow.builder()
.addHttpListener(port, "localhost"))
return server
}
@Override
void stopServer(UndertowJaxrsServer server) {
server.stop()
}
}

View File

@ -2,7 +2,7 @@ apply from: "$rootDir/gradle/instrumentation.gradle"
muzzle {
// Cant assert fails because muzzle assumes all instrumentations will fail
// Instrumentations in jaxrs-2.0 will pass
// Instrumentations in jaxrs-2.0-common will pass
// Resteasy changes a class's package in 3.1.0 then moves it back in 3.5.0 and then moves it forward again in 4.0.0
// so the jaxrs-2.0-resteasy-3.0 module applies to [3.0, 3.1) and [3.5, 4.0)
@ -22,7 +22,24 @@ muzzle {
dependencies {
compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
compileOnly group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.1.0.Final'
library group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.1.0.Final'
implementation project(':instrumentation:jaxrs:jaxrs-2.0')
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common')
testImplementation project(':instrumentation:servlet:servlet-3.0')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing')
testLibrary(group: 'org.jboss.resteasy', name: 'resteasy-undertow', version: '3.1.0.Final') {
exclude group: 'org.jboss.resteasy', module: 'resteasy-client'
}
// artifact name changed from 'resteasy-jaxrs' to 'resteasy-core' starting from version 4.0.0
latestDepTestLibrary group: 'org.jboss.resteasy', name: 'resteasy-core', version: '+'
}
if (findProperty('testLatestDeps')) {
configurations {
// artifact name changed from 'resteasy-jaxrs' to 'resteasy-core' starting from version 4.0.0
testImplementation.exclude group: 'org.jboss.resteasy', module: 'resteasy-jaxrs'
}
}

View File

@ -16,12 +16,16 @@
package io.opentelemetry.instrumentation.auto.jaxrs.v2_0;
import static io.opentelemetry.instrumentation.auto.jaxrs.v2_0.JaxRsAnnotationsTracer.TRACER;
import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.auto.api.SpanWithScope;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.tooling.Instrumenter;
import io.opentelemetry.trace.Span;
import java.lang.reflect.Method;
import javax.ws.rs.container.ContainerRequestContext;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.Local;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.interception.jaxrs.PostMatchContainerRequestContext;
@ -39,25 +43,31 @@ import org.jboss.resteasy.core.interception.jaxrs.PostMatchContainerRequestConte
public class Resteasy31RequestContextInstrumentation extends AbstractRequestContextInstrumentation {
public static class ContainerRequestContextAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope decorateAbortSpan(@Advice.This ContainerRequestContext context) {
public static void decorateAbortSpan(
@Advice.This ContainerRequestContext context,
@Local("otelSpan") Span span,
@Local("otelScope") Scope scope) {
if (context.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null
&& context instanceof PostMatchContainerRequestContext) {
ResourceMethodInvoker resourceMethodInvoker =
((PostMatchContainerRequestContext) context).getResourceMethod();
Method method = resourceMethodInvoker.getMethod();
Class resourceClass = resourceMethodInvoker.getResourceClass();
Class<?> resourceClass = resourceMethodInvoker.getResourceClass();
return RequestFilterHelper.createOrUpdateAbortSpan(context, resourceClass, method);
span = RequestFilterHelper.createOrUpdateAbortSpan(context, resourceClass, method);
if (span != null) {
scope = TRACER.startScope(span);
}
}
return null;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter SpanWithScope scope, @Advice.Thrown Throwable throwable) {
RequestFilterHelper.closeSpanAndScope(scope, throwable);
@Local("otelSpan") Span span,
@Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable) {
RequestFilterHelper.closeSpanAndScope(span, scope, throwable);
}
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ResteasyAnnotationInstrumentationTest extends JaxRsAnnotationsInstrumentationTest {
}

View File

@ -0,0 +1,54 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import static Resource.Test1
import static Resource.Test2
import static Resource.Test3
import javax.ws.rs.core.MediaType
import org.jboss.resteasy.mock.MockDispatcherFactory
import org.jboss.resteasy.mock.MockHttpRequest
import org.jboss.resteasy.mock.MockHttpResponse
import spock.lang.Shared
class ResteasyFilterTest extends JaxRsFilterTest {
@Shared
def dispatcher
def setupSpec() {
dispatcher = MockDispatcherFactory.createDispatcher()
def registry = dispatcher.getRegistry()
registry.addSingletonResource(new Test1())
registry.addSingletonResource(new Test2())
registry.addSingletonResource(new Test3())
dispatcher.getProviderFactory().register(simpleRequestFilter)
dispatcher.getProviderFactory().register(prematchRequestFilter)
}
@Override
def makeRequest(String url) {
MockHttpRequest request = MockHttpRequest.post(url)
request.contentType(MediaType.TEXT_PLAIN_TYPE)
request.content(new byte[0])
MockHttpResponse response = new MockHttpResponse()
dispatcher.invoke(request, response)
return [response.contentAsString, response.status]
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import io.undertow.Undertow
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer
class ResteasyHttpServerTest extends JaxRsHttpServerTest<UndertowJaxrsServer> {
@Override
UndertowJaxrsServer startServer(int port) {
def server = new UndertowJaxrsServer()
server.deploy(JaxRsTestApplication)
server.start(Undertow.builder()
.addHttpListener(port, "localhost"))
return server
}
@Override
void stopServer(UndertowJaxrsServer server) {
server.stop()
}
}

View File

@ -0,0 +1,16 @@
apply from: "$rootDir/gradle/java.gradle"
dependencies {
api project(':testing-common')
api group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
implementation deps.groovy
implementation deps.opentelemetryApi
implementation deps.spock
implementation deps.slf4j
implementation deps.testLogging
implementation project(':auto-api')
implementation project(':instrumentation-api')
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common')
}

View File

@ -28,7 +28,7 @@ import javax.ws.rs.POST
import javax.ws.rs.PUT
import javax.ws.rs.Path
class JaxRsAnnotations2InstrumentationTest extends AgentTestRunner {
abstract class JaxRsAnnotationsInstrumentationTest extends AgentTestRunner {
def "instrumentation can be used as root span and resource is set to METHOD PATH"() {
setup:

View File

@ -17,20 +17,13 @@
import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderServerTrace
import static io.opentelemetry.trace.Span.Kind.INTERNAL
import io.dropwizard.testing.junit.ResourceTestRule
import io.opentelemetry.auto.test.AgentTestRunner
import javax.ws.rs.client.Entity
import javax.ws.rs.container.ContainerRequestContext
import javax.ws.rs.container.ContainerRequestFilter
import javax.ws.rs.container.PreMatching
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response
import javax.ws.rs.ext.Provider
import org.jboss.resteasy.core.Dispatcher
import org.jboss.resteasy.mock.MockDispatcherFactory
import org.jboss.resteasy.mock.MockHttpRequest
import org.jboss.resteasy.mock.MockHttpResponse
import org.junit.ClassRule
import spock.lang.Shared
import spock.lang.Unroll
@ -52,12 +45,9 @@ abstract class JaxRsFilterTest extends AgentTestRunner {
def abort = abortNormal || abortPrematch
when:
def responseText
def responseStatus
// start a trace because the test doesn't go through any servlet or other instrumentation.
runUnderServerTrace("test.span") {
(responseText, responseStatus) = makeRequest(resource)
def (responseText, responseStatus) = runUnderServerTrace("test.span") {
makeRequest(resource)
}
then:
@ -109,12 +99,9 @@ abstract class JaxRsFilterTest extends AgentTestRunner {
prematchRequestFilter.abort = false
when:
def responseText
def responseStatus
// start a trace because the test doesn't go through any servlet or other instrumentation.
runUnderServerTrace("test.span") {
(responseText, responseStatus) = makeRequest(resource)
def (responseText, responseStatus) = runUnderServerTrace("test.span") {
makeRequest(resource)
}
then:
@ -176,51 +163,3 @@ abstract class JaxRsFilterTest extends AgentTestRunner {
}
}
}
class JerseyFilterTest extends JaxRsFilterTest {
@Shared
@ClassRule
ResourceTestRule resources = ResourceTestRule.builder()
.addResource(new Resource.Test1())
.addResource(new Resource.Test2())
.addResource(new Resource.Test3())
.addProvider(simpleRequestFilter)
.addProvider(prematchRequestFilter)
.build()
@Override
def makeRequest(String url) {
Response response = resources.client().target(url).request().post(Entity.text(""))
return [response.readEntity(String), response.statusInfo.statusCode]
}
}
class ResteasyFilterTest extends JaxRsFilterTest {
@Shared
Dispatcher dispatcher
def setupSpec() {
dispatcher = MockDispatcherFactory.createDispatcher()
def registry = dispatcher.getRegistry()
registry.addSingletonResource(new Resource.Test1())
registry.addSingletonResource(new Resource.Test2())
registry.addSingletonResource(new Resource.Test3())
dispatcher.getProviderFactory().register(simpleRequestFilter)
dispatcher.getProviderFactory().register(prematchRequestFilter)
}
@Override
def makeRequest(String url) {
MockHttpRequest request = MockHttpRequest.post(url)
request.contentType(MediaType.TEXT_PLAIN_TYPE)
request.content(new byte[0])
MockHttpResponse response = new MockHttpResponse()
dispatcher.invoke(request, response)
return [response.contentAsString, response.status]
}
}

View File

@ -20,19 +20,12 @@ import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCC
import static io.opentelemetry.trace.Span.Kind.INTERNAL
import static io.opentelemetry.trace.Span.Kind.SERVER
import io.dropwizard.jetty.NonblockingServletHolder
import io.opentelemetry.auto.test.asserts.TraceAssert
import io.opentelemetry.auto.test.base.HttpServerTest
import io.opentelemetry.instrumentation.api.MoreAttributes
import io.opentelemetry.sdk.trace.data.SpanData
import io.opentelemetry.trace.attributes.SemanticAttributes
import io.undertow.Undertow
import okhttp3.HttpUrl
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.ServletContextHandler
import org.glassfish.jersey.server.ResourceConfig
import org.glassfish.jersey.servlet.ServletContainer
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer
import spock.lang.Unroll
abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> {
@ -184,42 +177,4 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> {
}
}
class ResteasyHttpServerTest extends JaxRsHttpServerTest<UndertowJaxrsServer> {
@Override
UndertowJaxrsServer startServer(int port) {
def server = new UndertowJaxrsServer()
server.deploy(JaxRsTestApplication)
server.start(Undertow.builder()
.addHttpListener(port, "localhost"))
return server
}
@Override
void stopServer(UndertowJaxrsServer server) {
server.stop()
}
}
class JerseyHttpServerTest extends JaxRsHttpServerTest<Server> {
@Override
Server startServer(int port) {
def servlet = new ServletContainer(ResourceConfig.forApplicationClass(JaxRsTestApplication))
def handler = new ServletContextHandler(ServletContextHandler.SESSIONS)
handler.setContextPath("/")
handler.addServlet(new NonblockingServletHolder(servlet), "/*")
def server = new Server(port)
server.setHandler(handler)
server.start()
return server
}
@Override
void stopServer(Server httpServer) {
httpServer.stop()
}
}

View File

@ -1,62 +0,0 @@
apply from: "$rootDir/gradle/instrumentation.gradle"
apply plugin: 'org.unbroken-dome.test-sets'
muzzle {
fail {
group = "javax.ws.rs"
module = "jsr311-api"
versions = "[,]"
}
pass {
group = "javax.ws.rs"
module = "javax.ws.rs-api"
versions = "[,]"
}
}
testSets {
resteasy31Test {
dirName = 'test'
}
}
dependencies {
compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
testImplementation project(':instrumentation:servlet:servlet-3.0')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-jersey-2.0')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.0')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.1')
// Jersey
// First version with DropwizardTestSupport:
testLibrary group: 'io.dropwizard', name: 'dropwizard-testing', version: '0.8.0'
testImplementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.2.3'
testImplementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-afterburner', version: '2.9.10'
latestDepTestLibrary group: 'io.dropwizard', name: 'dropwizard-testing', version: '1.+'
// Resteasy
testLibrary group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.0.0.Final'
testImplementation(group: 'org.jboss.resteasy', name: 'resteasy-undertow', version: '3.0.4.Final') {
exclude group: 'org.jboss.resteasy', module: 'resteasy-client'
}
testImplementation group: 'io.undertow', name: 'undertow-servlet', version: '1.0.0.Final'
resteasy31TestImplementation(group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.1.0.Final')
resteasy31TestImplementation(group: 'org.jboss.resteasy', name: 'resteasy-undertow', version: '3.1.0.Final') {
exclude group: 'org.jboss.resteasy', module: 'resteasy-client'
}
latestDepTestLibrary(group: 'org.jboss.resteasy', name: 'resteasy-undertow', version: '3.+') {
exclude group: 'org.jboss.resteasy', module: 'resteasy-client'
}
// TODO: resteasy 4.+ has changed artifact name to resteasy-core
// latestDepTestLibrary group: 'org.jboss.resteasy', name: 'resteasy-core', version: '+'
// latestDepTestLibrary(group: 'org.jboss.resteasy', name: 'resteasy-undertow', version: '+') {
// exclude group: 'org.jboss.resteasy', module: 'resteasy-client'
// }
}
test.dependsOn resteasy31Test

View File

@ -100,10 +100,11 @@ include ':instrumentation:java-classloader:tomcat-testing'
include ':instrumentation:java-concurrent'
include ':instrumentation:java-httpclient'
include ':instrumentation:jaxrs:jaxrs-1.0'
include ':instrumentation:jaxrs:jaxrs-2.0'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-jersey-2.0'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.0'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.1'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing'
include ':instrumentation:jaxrs-client:jaxrs-client-1.1'
include ':instrumentation:jaxrs-client:jaxrs-client-2.0'
include ':instrumentation:jaxrs-client:jaxrs-client-2.0:jaxrs-client-2.0-jersey-2.0'