From d8c9639fbbe804f6d4d5a83065cc4ca546b8211d Mon Sep 17 00:00:00 2001 From: Richard Startin Date: Mon, 18 May 2020 09:59:37 +0100 Subject: [PATCH] Grizzly-http and grizzly-client instrumentation (DataDog/dd-trace-java#1365) --- .../bootstrap/InstrumentationContext.java | 6 +- .../bootstrap/instrumentation/api/Pair.java | 47 ++++ .../decorator/HttpServerDecorator.java | 1 + .../grizzly-client-1.9.gradle | 35 +++ .../grizzly/client/ClientDecorator.java | 47 ++++ .../grizzly/client/ClientRequestAdvice.java | 58 +++++ .../client/ClientRequestInstrumentation.java | 67 ++++++ .../grizzly/client/ClientResponseAdvice.java | 57 +++++ .../client/ClientResponseInstrumentation.java | 73 ++++++ .../grizzly/client/InjectAdapter.java | 29 +++ .../groovy/GrizzlyAsyncHttpClientTest.groovy | 85 +++++++ .../grizzly-http-2.3/grizzly-http-2.3.gradle | 28 +++ .../http/v2_3/DefaultFilterChainAdvice.java | 29 +++ .../DefaultFilterChainInstrumentation.java | 63 +++++ .../grizzly/http/v2_3/ExtractAdapter.java | 28 +++ .../grizzly/http/v2_3/FilterAdvice.java | 48 ++++ .../http/v2_3/FilterInstrumentation.java | 73 ++++++ .../grizzly/http/v2_3/GrizzlyDecorator.java | 115 +++++++++ .../http/v2_3/HttpCodecFilterAdvice.java | 30 +++ .../v2_3/HttpCodecFilterInstrumentation.java | 71 ++++++ .../http/v2_3/HttpCodecFilterOldAdvice.java | 31 +++ .../http/v2_3/HttpServerFilterAdvice.java | 29 +++ .../v2_3/HttpServerFilterInstrumentation.java | 63 +++++ .../src/test/groovy/GrizzlyAsyncTest.groovy | 101 ++++++++ .../GrizzlyFilterchainServerTest.groovy | 219 ++++++++++++++++++ ...yFilterchainServerTestInstrumentation.java | 40 ++++ .../test/groovy/GrizzlyIOStrategyTest.groovy | 66 ++++++ .../src/test/groovy/GrizzlyTest.groovy | 108 +++++++++ settings.gradle | 2 + .../auto/test/base/HttpServerTest.groovy | 5 + 30 files changed, 1652 insertions(+), 2 deletions(-) create mode 100644 agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/instrumentation/api/Pair.java create mode 100644 instrumentation/grizzly-client-1.9/grizzly-client-1.9.gradle create mode 100644 instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientDecorator.java create mode 100644 instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientRequestAdvice.java create mode 100644 instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientRequestInstrumentation.java create mode 100644 instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientResponseAdvice.java create mode 100644 instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientResponseInstrumentation.java create mode 100644 instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/InjectAdapter.java create mode 100644 instrumentation/grizzly-client-1.9/src/test/groovy/GrizzlyAsyncHttpClientTest.groovy create mode 100644 instrumentation/grizzly-http-2.3/grizzly-http-2.3.gradle create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/DefaultFilterChainAdvice.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/DefaultFilterChainInstrumentation.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/ExtractAdapter.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/FilterAdvice.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/FilterInstrumentation.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/GrizzlyDecorator.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterAdvice.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterInstrumentation.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterOldAdvice.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpServerFilterAdvice.java create mode 100644 instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpServerFilterInstrumentation.java create mode 100644 instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyAsyncTest.groovy create mode 100644 instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyFilterchainServerTest.groovy create mode 100644 instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyFilterchainServerTestInstrumentation.java create mode 100644 instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyIOStrategyTest.groovy create mode 100644 instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyTest.groovy diff --git a/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/InstrumentationContext.java b/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/InstrumentationContext.java index 5fb45c02dd..ee2d9914ce 100644 --- a/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/InstrumentationContext.java +++ b/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/InstrumentationContext.java @@ -25,8 +25,10 @@ public class InstrumentationContext { *

Conceptually this can be thought of as a map lookup to fetch a second level map given * keyClass. * - *

However, the implementation is actually provided by bytecode transformation for performance - * reasons. + *

In reality, the calls to this method are re-written to something more performant + * while injecting advice into a method. + * + *

This method must only be called within an Advice class. * * @param keyClass The key class context is attached to. * @param contextClass The context class attached to the user class. diff --git a/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/instrumentation/api/Pair.java b/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/instrumentation/api/Pair.java new file mode 100644 index 0000000000..dedb1ae149 --- /dev/null +++ b/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/instrumentation/api/Pair.java @@ -0,0 +1,47 @@ +/* + * 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. + */ +package io.opentelemetry.auto.bootstrap.instrumentation.api; + +public final class Pair { + + public static Pair of(final T left, final U right) { + return new Pair<>(left, right); + } + + private final T left; + private final U right; + + Pair(final T left, final U right) { + this.left = left; + this.right = right; + } + + public T getLeft() { + return left; + } + + public U getRight() { + return right; + } + + public boolean hasLeft() { + return null != left; + } + + public boolean hasRight() { + return null != right; + } +} diff --git a/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/instrumentation/decorator/HttpServerDecorator.java b/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/instrumentation/decorator/HttpServerDecorator.java index 535c66b935..9b153ba20d 100644 --- a/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/instrumentation/decorator/HttpServerDecorator.java +++ b/agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/instrumentation/decorator/HttpServerDecorator.java @@ -27,6 +27,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public abstract class HttpServerDecorator extends ServerDecorator { public static final String SPAN_ATTRIBUTE = "io.opentelemetry.auto.span"; + public static final String RESPONSE_ATTRIBUTE = "io.opentelemetry.auto.response"; public static final String DEFAULT_SPAN_NAME = "HTTP request"; protected abstract String method(REQUEST request); diff --git a/instrumentation/grizzly-client-1.9/grizzly-client-1.9.gradle b/instrumentation/grizzly-client-1.9/grizzly-client-1.9.gradle new file mode 100644 index 0000000000..080b739fd1 --- /dev/null +++ b/instrumentation/grizzly-client-1.9/grizzly-client-1.9.gradle @@ -0,0 +1,35 @@ +ext { + minJavaVersionForTests = JavaVersion.VERSION_1_8 +} + +apply from: "${rootDir}/gradle/instrumentation.gradle" +apply plugin: 'org.unbroken-dome.test-sets' + +muzzle { + pass { + group = "org.glassfish.grizzly" + module = "grizzly-http-client" + versions = "[1.9,1.16]" + assertInverse = true + } + pass { + group = "com.ning" + module = "async-http-client" + versions = "[1.9.0,)" + assertInverse = true + } +} + +testSets { + latestDepTest { + dirName = 'test' + } +} + +dependencies { + compileOnly group: 'org.glassfish.grizzly', name: 'grizzly-http-client', version: '1.9' + // for some reason, the tests don't *load* until 1.12, but muzzles works as far back as 1.9 + testCompile group: 'org.glassfish.grizzly', name: 'grizzly-http-client', version: '1.12' + + latestDepTestCompile group: 'org.glassfish.grizzly', name: 'grizzly-http-client', version: '1.16' +} diff --git a/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientDecorator.java b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientDecorator.java new file mode 100644 index 0000000000..0b50aeb175 --- /dev/null +++ b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientDecorator.java @@ -0,0 +1,47 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.client; + +import com.ning.http.client.Request; +import com.ning.http.client.Response; +import io.opentelemetry.OpenTelemetry; +import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpClientDecorator; +import io.opentelemetry.trace.Tracer; +import java.net.URI; +import java.net.URISyntaxException; + +public class ClientDecorator extends HttpClientDecorator { + + public static final ClientDecorator DECORATE = new ClientDecorator(); + + public static final Tracer TRACER = + OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.grizzly-client-1.9"); + + @Override + protected String method(final Request request) { + return request.getMethod(); + } + + @Override + protected URI url(final Request request) throws URISyntaxException { + return request.getUri().toJavaNetURI(); + } + + @Override + protected Integer status(final Response response) { + return response.getStatusCode(); + } +} diff --git a/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientRequestAdvice.java b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientRequestAdvice.java new file mode 100644 index 0000000000..e04283bf03 --- /dev/null +++ b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientRequestAdvice.java @@ -0,0 +1,58 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.client; + +import static io.opentelemetry.auto.instrumentation.grizzly.client.ClientDecorator.DECORATE; +import static io.opentelemetry.auto.instrumentation.grizzly.client.ClientDecorator.TRACER; +import static io.opentelemetry.auto.instrumentation.grizzly.client.InjectAdapter.SETTER; +import static io.opentelemetry.trace.Span.Kind.CLIENT; +import static io.opentelemetry.trace.TracingContextUtils.withSpan; + +import com.ning.http.client.AsyncHandler; +import com.ning.http.client.Request; +import io.grpc.Context; +import io.opentelemetry.OpenTelemetry; +import io.opentelemetry.auto.bootstrap.InstrumentationContext; +import io.opentelemetry.auto.bootstrap.instrumentation.api.Pair; +import io.opentelemetry.context.Scope; +import io.opentelemetry.trace.Span; +import net.bytebuddy.asm.Advice; + +public class ClientRequestAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope onEnter( + @Advice.Argument(0) final Request request, + @Advice.Argument(1) final AsyncHandler handler) { + final Span parentSpan = TRACER.getCurrentSpan(); + final Span span = + TRACER.spanBuilder(DECORATE.spanNameForRequest(request)).setSpanKind(CLIENT).startSpan(); + DECORATE.afterStart(span); + DECORATE.onRequest(span, request); + + final Context context = withSpan(span, Context.current()); + OpenTelemetry.getPropagators().getHttpTextFormat().inject(context, request, SETTER); + InstrumentationContext.get(AsyncHandler.class, Pair.class) + .put(handler, Pair.of(parentSpan, span)); + return TRACER.withSpan(span); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.Enter final Scope scope) { + // span closed in ClientResponseAdvice + scope.close(); + } +} diff --git a/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientRequestInstrumentation.java b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientRequestInstrumentation.java new file mode 100644 index 0000000000..027ee1b737 --- /dev/null +++ b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientRequestInstrumentation.java @@ -0,0 +1,67 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.client; + +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import io.opentelemetry.auto.bootstrap.instrumentation.api.Pair; +import io.opentelemetry.auto.tooling.Instrumenter; +import java.util.Map; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public final class ClientRequestInstrumentation extends Instrumenter.Default { + + public ClientRequestInstrumentation() { + super("grizzly-client", "ning"); + } + + @Override + public Map contextStore() { + return singletonMap("com.ning.http.client.AsyncHandler", Pair.class.getName()); + } + + @Override + public ElementMatcher typeMatcher() { + return named("com.ning.http.client.AsyncHttpClient"); + } + + @Override + public String[] helperClassNames() { + return new String[] {packageName + ".ClientDecorator", packageName + ".InjectAdapter"}; + } + + @Override + protected boolean defaultEnabled() { + return false; + } + + @Override + public Map, String> transformers() { + return singletonMap( + named("executeRequest") + .and(takesArgument(0, named("com.ning.http.client.Request"))) + .and(takesArgument(1, named("com.ning.http.client.AsyncHandler"))) + .and(isPublic()), + packageName + ".ClientRequestAdvice"); + } +} diff --git a/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientResponseAdvice.java b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientResponseAdvice.java new file mode 100644 index 0000000000..533eb412cd --- /dev/null +++ b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientResponseAdvice.java @@ -0,0 +1,57 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.client; + +import static io.opentelemetry.auto.instrumentation.grizzly.client.ClientDecorator.DECORATE; +import static io.opentelemetry.auto.instrumentation.grizzly.client.ClientDecorator.TRACER; + +import com.ning.http.client.AsyncCompletionHandler; +import com.ning.http.client.AsyncHandler; +import com.ning.http.client.Response; +import io.opentelemetry.auto.bootstrap.ContextStore; +import io.opentelemetry.auto.bootstrap.InstrumentationContext; +import io.opentelemetry.auto.bootstrap.instrumentation.api.Pair; +import io.opentelemetry.context.Scope; +import io.opentelemetry.trace.Span; +import net.bytebuddy.asm.Advice; + +public class ClientResponseAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope onEnter( + @Advice.This final AsyncCompletionHandler handler, + @Advice.Argument(0) final Response response) { + final ContextStore contextStore = + InstrumentationContext.get(AsyncHandler.class, Pair.class); + final Pair spanWithParent = contextStore.get(handler); + if (null != spanWithParent) { + contextStore.put(handler, null); + } + if (spanWithParent.hasRight()) { + DECORATE.onResponse(spanWithParent.getRight(), response); + DECORATE.beforeFinish(spanWithParent.getRight()); + spanWithParent.getRight().end(); + } + return spanWithParent.hasLeft() ? TRACER.withSpan(spanWithParent.getLeft()) : null; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.Enter final Scope scope) { + if (null != scope) { + scope.close(); + } + } +} diff --git a/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientResponseInstrumentation.java b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientResponseInstrumentation.java new file mode 100644 index 0000000000..1c7a948736 --- /dev/null +++ b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/ClientResponseInstrumentation.java @@ -0,0 +1,73 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.client; + +import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed; +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import io.opentelemetry.auto.bootstrap.instrumentation.api.Pair; +import io.opentelemetry.auto.tooling.Instrumenter; +import java.util.Map; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public final class ClientResponseInstrumentation extends Instrumenter.Default { + + public ClientResponseInstrumentation() { + super("grizzly-client", "ning"); + } + + @Override + public Map contextStore() { + return singletonMap("com.ning.http.client.AsyncHandler", Pair.class.getName()); + } + + @Override + public ElementMatcher classLoaderMatcher() { + return hasClassesNamed("com.ning.http.client.AsyncCompletionHandler"); + } + + @Override + protected boolean defaultEnabled() { + return false; + } + + @Override + public ElementMatcher typeMatcher() { + return hasSuperClass(named("com.ning.http.client.AsyncCompletionHandler")); + } + + @Override + public String[] helperClassNames() { + return new String[] {packageName + ".ClientDecorator"}; + } + + @Override + public Map, String> transformers() { + return singletonMap( + named("onCompleted") + .and(takesArgument(0, named("com.ning.http.client.Response"))) + .and(isPublic()), + packageName + ".ClientResponseAdvice"); + } +} diff --git a/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/InjectAdapter.java b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/InjectAdapter.java new file mode 100644 index 0000000000..00e149cbb8 --- /dev/null +++ b/instrumentation/grizzly-client-1.9/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/client/InjectAdapter.java @@ -0,0 +1,29 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.client; + +import com.ning.http.client.Request; +import io.opentelemetry.context.propagation.HttpTextFormat; + +public class InjectAdapter implements HttpTextFormat.Setter { + + public static final InjectAdapter SETTER = new InjectAdapter(); + + @Override + public void set(final Request carrier, final String key, final String value) { + carrier.getHeaders().replaceWith(key, value); + } +} diff --git a/instrumentation/grizzly-client-1.9/src/test/groovy/GrizzlyAsyncHttpClientTest.groovy b/instrumentation/grizzly-client-1.9/src/test/groovy/GrizzlyAsyncHttpClientTest.groovy new file mode 100644 index 0000000000..bc6758af3a --- /dev/null +++ b/instrumentation/grizzly-client-1.9/src/test/groovy/GrizzlyAsyncHttpClientTest.groovy @@ -0,0 +1,85 @@ +/* + * 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 com.ning.http.client.AsyncCompletionHandler +import com.ning.http.client.AsyncHttpClient +import com.ning.http.client.Request +import com.ning.http.client.RequestBuilder +import com.ning.http.client.Response +import com.ning.http.client.uri.Uri +import io.opentelemetry.auto.test.base.HttpClientTest +import spock.lang.AutoCleanup +import spock.lang.Shared + +class GrizzlyAsyncHttpClientTest extends HttpClientTest { + + static { + System.setProperty("ota.integration.grizzly-client.enabled", "true") + } + + @AutoCleanup + @Shared + def client = new AsyncHttpClient() + + @Override + int doRequest(String method, URI uri, Map headers, Closure callback) { + + RequestBuilder requestBuilder = new RequestBuilder(method) + .setUri(Uri.create(uri.toString())) + headers.entrySet().each { + requestBuilder.addHeader(it.key, it.value) + } + Request request = requestBuilder.build() + + def handler = new AsyncCompletionHandlerMock(callback) + + def response = client.executeRequest(request, handler).get() + response.statusCode + } + + class AsyncCompletionHandlerMock extends AsyncCompletionHandler { + + private Closure callback + + AsyncCompletionHandlerMock(Closure callback) { + this.callback = callback + } + + @Override + Response onCompleted(Response response) throws Exception { + if (callback != null) { + callback() + } + return response + } + } + + @Override + boolean testRedirects() { + false + } + + @Override + boolean testConnectionFailure() { + false + } + + @Override + boolean testRemoteConnection() { + return false + } +} + + diff --git a/instrumentation/grizzly-http-2.3/grizzly-http-2.3.gradle b/instrumentation/grizzly-http-2.3/grizzly-http-2.3.gradle new file mode 100644 index 0000000000..310ca9b06c --- /dev/null +++ b/instrumentation/grizzly-http-2.3/grizzly-http-2.3.gradle @@ -0,0 +1,28 @@ +apply from: "${rootDir}/gradle/instrumentation.gradle" +apply plugin: 'org.unbroken-dome.test-sets' + +muzzle { + pass { + group = "org.glassfish.grizzly" + module = 'grizzly-http' + versions = "[2.3,)" + } +} + +testSets { + latestDepTest { + dirName = 'test' + } +} + +dependencies { + + compileOnly group: 'org.glassfish.grizzly', name: 'grizzly-http', version: '2.3' + + testCompile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.2.3' + testCompile group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0' + testCompile group: 'org.glassfish.jersey.containers', name: 'jersey-container-grizzly2-http', version: '2.0' + + latestDepTestCompile group: 'org.glassfish.jersey.containers', name: 'jersey-container-grizzly2-http', version: '2.+' + latestDepTestCompile group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.+' +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/DefaultFilterChainAdvice.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/DefaultFilterChainAdvice.java new file mode 100644 index 0000000000..0225b73051 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/DefaultFilterChainAdvice.java @@ -0,0 +1,29 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import net.bytebuddy.asm.Advice; +import org.glassfish.grizzly.filterchain.FilterChainContext; + +public class DefaultFilterChainAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onFail( + @Advice.Argument(0) final FilterChainContext ctx, + @Advice.Argument(1) final Throwable throwable) { + GrizzlyDecorator.onFilterChainFail(ctx, throwable); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/DefaultFilterChainInstrumentation.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/DefaultFilterChainInstrumentation.java new file mode 100644 index 0000000000..fdbae2b97e --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/DefaultFilterChainInstrumentation.java @@ -0,0 +1,63 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPrivate; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import io.opentelemetry.auto.tooling.Instrumenter; +import java.util.Collections; +import java.util.Map; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class DefaultFilterChainInstrumentation extends Instrumenter.Default { + + public DefaultFilterChainInstrumentation() { + super("grizzly-filterchain"); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.glassfish.grizzly.filterchain.DefaultFilterChain"); + } + + @Override + public String[] helperClassNames() { + return new String[] {packageName + ".GrizzlyDecorator", packageName + ".ExtractAdapter"}; + } + + @Override + protected boolean defaultEnabled() { + return false; + } + + @Override + public Map, String> transformers() { + return Collections.singletonMap( + isMethod() + .and(isPrivate()) + .and(named("notifyFailure")) + .and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext"))) + .and(takesArgument(1, named("java.lang.Throwable"))), + packageName + ".DefaultFilterChainAdvice"); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/ExtractAdapter.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/ExtractAdapter.java new file mode 100644 index 0000000000..2cf9c09469 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/ExtractAdapter.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import io.opentelemetry.context.propagation.HttpTextFormat; +import org.glassfish.grizzly.http.HttpHeader; + +public class ExtractAdapter implements HttpTextFormat.Getter { + public static final ExtractAdapter GETTER = new ExtractAdapter(); + + @Override + public String get(final HttpHeader carrier, final String key) { + return carrier.getHeader(key); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/FilterAdvice.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/FilterAdvice.java new file mode 100644 index 0000000000..d0c9085bca --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/FilterAdvice.java @@ -0,0 +1,48 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import static io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpServerDecorator.SPAN_ATTRIBUTE; +import static io.opentelemetry.auto.instrumentation.grizzly.http.v2_3.GrizzlyDecorator.TRACER; + +import io.opentelemetry.context.Scope; +import io.opentelemetry.trace.Span; +import net.bytebuddy.asm.Advice; +import org.glassfish.grizzly.filterchain.BaseFilter; +import org.glassfish.grizzly.filterchain.FilterChainContext; + +public class FilterAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope onEnter( + @Advice.This final BaseFilter it, @Advice.Argument(0) final FilterChainContext ctx) { + if (TRACER.getCurrentSpan().getContext().isValid()) { + return null; + } + final Span span = (Span) ctx.getAttributes().getAttribute(SPAN_ATTRIBUTE); + if (span == null) { + return null; + } + return TRACER.withSpan(span); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.This final BaseFilter it, @Advice.Enter final Scope scope) { + if (scope != null) { + scope.close(); + } + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/FilterInstrumentation.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/FilterInstrumentation.java new file mode 100644 index 0000000000..ac8f307624 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/FilterInstrumentation.java @@ -0,0 +1,73 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed; +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import io.opentelemetry.auto.tooling.Instrumenter; +import java.util.Map; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +@AutoService(Instrumenter.class) +public final class FilterInstrumentation extends Instrumenter.Default { + + public FilterInstrumentation() { + super("grizzly-filterchain"); + } + + @Override + public ElementMatcher classLoaderMatcher() { + return hasClassesNamed("org.glassfish.grizzly.filterchain.BaseFilter"); + } + + @Override + public ElementMatcher typeMatcher() { + return hasSuperClass(named("org.glassfish.grizzly.filterchain.BaseFilter")) + // HttpCodecFilter is instrumented in the server instrumentation + .and( + not( + ElementMatchers.named( + "org.glassfish.grizzly.http.HttpCodecFilter"))) + .and( + not( + ElementMatchers.named( + "org.glassfish.grizzly.http.HttpServerFilter"))); + } + + @Override + public String[] helperClassNames() { + return new String[] {packageName + ".GrizzlyDecorator", packageName + ".ExtractAdapter"}; + } + + @Override + public Map, String> transformers() { + return singletonMap( + named("handleRead") + .and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext"))) + .and(isPublic()), + packageName + ".FilterAdvice"); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/GrizzlyDecorator.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/GrizzlyDecorator.java new file mode 100644 index 0000000000..2cb718f020 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/GrizzlyDecorator.java @@ -0,0 +1,115 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import static io.opentelemetry.trace.Span.Kind.SERVER; + +import io.opentelemetry.OpenTelemetry; +import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpServerDecorator; +import io.opentelemetry.context.Scope; +import io.opentelemetry.trace.Span; +import io.opentelemetry.trace.Tracer; +import java.net.URI; +import java.net.URISyntaxException; +import org.glassfish.grizzly.filterchain.FilterChainContext; +import org.glassfish.grizzly.http.HttpHeader; +import org.glassfish.grizzly.http.HttpRequestPacket; +import org.glassfish.grizzly.http.HttpResponsePacket; + +public class GrizzlyDecorator + extends HttpServerDecorator { + + public static final GrizzlyDecorator DECORATE = new GrizzlyDecorator(); + + public static final Tracer TRACER = + OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.grizzly-http-2.3"); + + @Override + protected String method(final HttpRequestPacket httpRequest) { + return httpRequest.getMethod().getMethodString(); + } + + @Override + protected URI url(final HttpRequestPacket httpRequest) throws URISyntaxException { + return new URI( + (httpRequest.isSecure() ? "https://" : "http://") + + httpRequest.getRemoteHost() + + ":" + + httpRequest.getLocalPort() + + httpRequest.getRequestURI() + + (httpRequest.getQueryString() != null ? "?" + httpRequest.getQueryString() : "")); + } + + @Override + protected String peerHostIP(final HttpRequestPacket httpRequest) { + return httpRequest.getLocalHost(); + } + + @Override + protected Integer peerPort(final HttpRequestPacket httpRequest) { + return httpRequest.getLocalPort(); + } + + @Override + protected Integer status(final HttpResponsePacket httpResponse) { + return httpResponse.getStatus(); + } + + public static void onHttpServerFilterPrepareResponseExit( + final FilterChainContext ctx, final HttpResponsePacket responsePacket) { + final Span span = (Span) ctx.getAttributes().getAttribute(SPAN_ATTRIBUTE); + DECORATE.onResponse(span, responsePacket); + span.end(); + ctx.getAttributes().removeAttribute(SPAN_ATTRIBUTE); + ctx.getAttributes().removeAttribute(RESPONSE_ATTRIBUTE); + } + + public static void onHttpCodecFilterExit( + final FilterChainContext ctx, final HttpHeader httpHeader) { + // only create a span if there isn't another one attached to the current ctx + // and if the httpHeader has been parsed into a HttpRequestPacket + if (ctx.getAttributes().getAttribute(SPAN_ATTRIBUTE) != null + || !(httpHeader instanceof HttpRequestPacket)) { + return; + } + final HttpRequestPacket httpRequest = (HttpRequestPacket) httpHeader; + final HttpResponsePacket httpResponse = httpRequest.getResponse(); + final Span span = + TRACER + .spanBuilder(DECORATE.spanNameForRequest(httpRequest)) + .setSpanKind(SERVER) + .setParent(extract(httpHeader, ExtractAdapter.GETTER)) + .startSpan(); + try (final Scope ignored = TRACER.withSpan(span)) { + DECORATE.afterStart(span); + ctx.getAttributes().setAttribute(SPAN_ATTRIBUTE, span); + ctx.getAttributes().setAttribute(RESPONSE_ATTRIBUTE, httpResponse); + DECORATE.onConnection(span, httpRequest); + DECORATE.onRequest(span, httpRequest); + } + } + + public static void onFilterChainFail(final FilterChainContext ctx, final Throwable throwable) { + final Span span = (Span) ctx.getAttributes().getAttribute(SPAN_ATTRIBUTE); + if (null != span) { + DECORATE.onError(span, throwable); + DECORATE.beforeFinish(span); + span.end(); + } + ctx.getAttributes().removeAttribute(SPAN_ATTRIBUTE); + ctx.getAttributes().removeAttribute(RESPONSE_ATTRIBUTE); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterAdvice.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterAdvice.java new file mode 100644 index 0000000000..4733e7225d --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterAdvice.java @@ -0,0 +1,30 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import net.bytebuddy.asm.Advice; +import org.glassfish.grizzly.filterchain.FilterChainContext; +import org.glassfish.grizzly.http.HttpHeader; + +public class HttpCodecFilterAdvice { + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.Argument(0) final FilterChainContext ctx, + @Advice.Argument(1) final HttpHeader httpHeader) { + GrizzlyDecorator.onHttpCodecFilterExit(ctx, httpHeader); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterInstrumentation.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterInstrumentation.java new file mode 100644 index 0000000000..c5cd077a90 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterInstrumentation.java @@ -0,0 +1,71 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import io.opentelemetry.auto.tooling.Instrumenter; +import java.util.HashMap; +import java.util.Map; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public final class HttpCodecFilterInstrumentation extends Instrumenter.Default { + + public HttpCodecFilterInstrumentation() { + super("grizzly-filterchain"); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.glassfish.grizzly.http.HttpCodecFilter"); + } + + @Override + protected boolean defaultEnabled() { + return false; + } + + @Override + public String[] helperClassNames() { + return new String[] {packageName + ".GrizzlyDecorator", packageName + ".ExtractAdapter"}; + } + + @Override + public Map, String> transformers() { + final Map, String> transformers = new HashMap<>(); + // this is for 2.3 through 2.3.19 + transformers.put( + named("handleRead") + .and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext"))) + .and(takesArgument(1, named("org.glassfish.grizzly.http.HttpPacketParsing"))) + .and(isPublic()), + packageName + ".HttpCodecFilterOldAdvice"); + // this is for 2.3.20+ + transformers.put( + named("handleRead") + .and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext"))) + .and(takesArgument(1, named("org.glassfish.grizzly.http.HttpHeader"))) + .and(isPublic()), + packageName + ".HttpCodecFilterAdvice"); + return transformers; + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterOldAdvice.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterOldAdvice.java new file mode 100644 index 0000000000..4baf8662f0 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpCodecFilterOldAdvice.java @@ -0,0 +1,31 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import net.bytebuddy.asm.Advice; +import org.glassfish.grizzly.filterchain.FilterChainContext; +import org.glassfish.grizzly.http.HttpHeader; +import org.glassfish.grizzly.http.HttpPacketParsing; + +public class HttpCodecFilterOldAdvice { + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.Argument(0) final FilterChainContext ctx, + @Advice.Argument(1) final HttpPacketParsing httpHeader) { + GrizzlyDecorator.onHttpCodecFilterExit(ctx, (HttpHeader) httpHeader); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpServerFilterAdvice.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpServerFilterAdvice.java new file mode 100644 index 0000000000..ba95845c10 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpServerFilterAdvice.java @@ -0,0 +1,29 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import net.bytebuddy.asm.Advice; +import org.glassfish.grizzly.filterchain.FilterChainContext; +import org.glassfish.grizzly.http.HttpResponsePacket; + +public class HttpServerFilterAdvice { + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.Argument(0) final FilterChainContext ctx, + @Advice.Argument(2) final HttpResponsePacket response) { + GrizzlyDecorator.onHttpServerFilterPrepareResponseExit(ctx, response); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpServerFilterInstrumentation.java b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpServerFilterInstrumentation.java new file mode 100644 index 0000000000..d7a5f99dd7 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/main/java/io/opentelemetry/auto/instrumentation/grizzly/http/v2_3/HttpServerFilterInstrumentation.java @@ -0,0 +1,63 @@ +/* + * 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. + */ +package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3; + +import static net.bytebuddy.matcher.ElementMatchers.isPrivate; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import io.opentelemetry.auto.tooling.Instrumenter; +import java.util.Collections; +import java.util.Map; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(Instrumenter.class) +public class HttpServerFilterInstrumentation extends Instrumenter.Default { + + public HttpServerFilterInstrumentation() { + super("grizzly-filterchain"); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.glassfish.grizzly.http.HttpServerFilter"); + } + + @Override + protected boolean defaultEnabled() { + return false; + } + + @Override + public String[] helperClassNames() { + return new String[] {packageName + ".GrizzlyDecorator", packageName + ".ExtractAdapter"}; + } + + @Override + public Map, String> transformers() { + return Collections.singletonMap( + named("prepareResponse") + .and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext"))) + .and(takesArgument(1, named("org.glassfish.grizzly.http.HttpRequestPacket"))) + .and(takesArgument(2, named("org.glassfish.grizzly.http.HttpResponsePacket"))) + .and(takesArgument(3, named("org.glassfish.grizzly.http.HttpContent"))) + .and(isPrivate()), + packageName + ".HttpServerFilterAdvice"); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyAsyncTest.groovy b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyAsyncTest.groovy new file mode 100644 index 0000000000..0eb0aaab56 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyAsyncTest.groovy @@ -0,0 +1,101 @@ +/* + * 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 org.glassfish.grizzly.http.server.HttpServer +import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory +import org.glassfish.jersey.server.ResourceConfig + +import javax.ws.rs.GET +import javax.ws.rs.Path +import javax.ws.rs.QueryParam +import javax.ws.rs.container.AsyncResponse +import javax.ws.rs.container.Suspended +import javax.ws.rs.core.Response +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.EXCEPTION +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.REDIRECT +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS + +class GrizzlyAsyncTest extends GrizzlyTest { + + @Override + HttpServer startServer(int port) { + ResourceConfig rc = new ResourceConfig() + rc.register(SimpleExceptionMapper) + rc.register(AsyncServiceResource) + GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc) + } + + @Path("/") + static class AsyncServiceResource { + private ExecutorService executor = Executors.newSingleThreadExecutor() + + @GET + @Path("success") + void success(@Suspended AsyncResponse ar) { + executor.execute { + controller(SUCCESS) { + ar.resume(Response.status(SUCCESS.status).entity(SUCCESS.body).build()) + } + } + } + + @GET + @Path("query") + Response query_param(@QueryParam("some") String param, @Suspended AsyncResponse ar) { + controller(QUERY_PARAM) { + ar.resume(Response.status(QUERY_PARAM.status).entity("some=$param".toString()).build()) + } + } + + @GET + @Path("redirect") + void redirect(@Suspended AsyncResponse ar) { + executor.execute { + controller(REDIRECT) { + ar.resume(Response.status(REDIRECT.status).location(new URI(REDIRECT.body)).build()) + } + } + } + + @GET + @Path("error-status") + void error(@Suspended AsyncResponse ar) { + executor.execute { + controller(ERROR) { + ar.resume(Response.status(ERROR.status).entity(ERROR.body).build()) + } + } + } + + @GET + @Path("exception") + void exception(@Suspended AsyncResponse ar) { + executor.execute { + try { + controller(EXCEPTION) { + throw new Exception(EXCEPTION.body) + } + } catch (Exception e) { + ar.resume(e) + } + } + } + } +} diff --git a/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyFilterchainServerTest.groovy b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyFilterchainServerTest.groovy new file mode 100644 index 0000000000..70d4c2d657 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyFilterchainServerTest.groovy @@ -0,0 +1,219 @@ +/* + * 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.opentelemetry.auto.test.base.HttpServerTest +import org.glassfish.grizzly.filterchain.BaseFilter +import org.glassfish.grizzly.filterchain.FilterChain +import org.glassfish.grizzly.filterchain.FilterChainBuilder +import org.glassfish.grizzly.filterchain.FilterChainContext +import org.glassfish.grizzly.filterchain.NextAction +import org.glassfish.grizzly.filterchain.TransportFilter +import org.glassfish.grizzly.http.HttpContent +import org.glassfish.grizzly.http.HttpHeader +import org.glassfish.grizzly.http.HttpRequestPacket +import org.glassfish.grizzly.http.HttpResponsePacket +import org.glassfish.grizzly.http.HttpServerFilter +import org.glassfish.grizzly.http.server.HttpServer +import org.glassfish.grizzly.nio.transport.TCPNIOServerConnection +import org.glassfish.grizzly.nio.transport.TCPNIOTransport +import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder +import org.glassfish.grizzly.utils.DelayedExecutor +import org.glassfish.grizzly.utils.IdleTimeoutFilter + +import java.util.concurrent.Executors + +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.AUTH_REQUIRED +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.EXCEPTION +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.REDIRECT +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS +import static java.lang.String.valueOf +import static java.nio.charset.Charset.defaultCharset +import static java.util.concurrent.TimeUnit.MILLISECONDS +import static org.glassfish.grizzly.memory.Buffers.wrap + +class GrizzlyFilterchainServerTest extends HttpServerTest { + + static { + System.setProperty("ota.integration.grizzly-filterchain.enabled", "true") + } + + private TCPNIOTransport transport + private TCPNIOServerConnection serverConnection + + @Override + HttpServer startServer(int port) { + FilterChain filterChain = setUpFilterChain() + setUpTransport(filterChain) + + serverConnection = transport.bind("127.0.0.1", port) + transport.start() + return null + } + + @Override + void stopServer(HttpServer httpServer) { + transport.shutdownNow() + } + + @Override + boolean testException() { + // justification: grizzly async closes the channel which + // looks like a ConnectException to the client when this happens + false + } + + void setUpTransport(FilterChain filterChain) { + TCPNIOTransportBuilder transportBuilder = TCPNIOTransportBuilder.newInstance() + .setOptimizedForMultiplexing(true) + + transportBuilder.setTcpNoDelay(true) + transportBuilder.setKeepAlive(false) + transportBuilder.setReuseAddress(true) + transportBuilder.setServerConnectionBackLog(50) + transportBuilder.setServerSocketSoTimeout(80000) + + transport = transportBuilder.build() + transport.setProcessor(filterChain) + } + + FilterChain setUpFilterChain() { + return FilterChainBuilder.stateless() + .add(createTransportFilter()) + .add(createIdleTimeoutFilter()) + .add(new HttpServerFilter()) + .add(new LastFilter()) + .build() + } + + TransportFilter createTransportFilter() { + return new TransportFilter() + } + + IdleTimeoutFilter createIdleTimeoutFilter() { + return new IdleTimeoutFilter(new DelayedExecutor(Executors.newCachedThreadPool()), 80000, MILLISECONDS) + } + + class LastFilter extends BaseFilter { + + @Override + NextAction handleRead(final FilterChainContext ctx) throws IOException { + if (ctx.getMessage() instanceof HttpContent) { + final HttpContent httpContent = ctx.getMessage() + final HttpHeader httpHeader = httpContent.getHttpHeader() + if (httpHeader instanceof HttpRequestPacket) { + HttpRequestPacket request = (HttpRequestPacket) httpContent.getHttpHeader() + ResponseParameters responseParameters = buildResponse(request) + HttpResponsePacket.Builder builder = HttpResponsePacket.builder(request) + .status(responseParameters.getStatus()) + .header("Content-Length", valueOf(responseParameters.getResponseBody().length)) + responseParameters.fillHeaders(builder) + HttpResponsePacket responsePacket = builder.build() + controller(responseParameters.getEndpoint()) { + ctx.write(HttpContent.builder(responsePacket) + .content(wrap(ctx.getMemoryManager(), responseParameters.getResponseBody())) + .build()) + } + } + } + return ctx.getStopAction() + } + + ResponseParameters buildResponse(HttpRequestPacket request) { + final String uri = request.getRequestURI() + final String requestParams = request.getQueryString() + final String fullPath = uri + (requestParams != null ? "?" + requestParams : "") + + Map headers = new HashMap<>() + + HttpServerTest.ServerEndpoint endpoint + switch (fullPath) { + case "/success": + endpoint = SUCCESS + break + case "/redirect": + endpoint = REDIRECT + headers.put("location", REDIRECT.body) + break + case "/error-status": + endpoint = ERROR + break + case "/exception": + throw new Exception(EXCEPTION.body) + case "/notFound": + endpoint = NOT_FOUND + break + case "/query?some=query": + endpoint = QUERY_PARAM + break + case "/path/123/param": + endpoint = PATH_PARAM + break + case "/authRequired": + endpoint = AUTH_REQUIRED + break + default: + endpoint = NOT_FOUND + break + } + + int status = endpoint.status + String responseBody = endpoint == REDIRECT ? "" : endpoint.body + + final byte[] responseBodyBytes = responseBody.getBytes(defaultCharset()) + return new ResponseParameters(endpoint, status, responseBodyBytes, headers) + } + + class ResponseParameters { + Map headers + HttpServerTest.ServerEndpoint endpoint + int status + byte[] responseBody + + ResponseParameters(HttpServerTest.ServerEndpoint endpoint, + int status, + byte[] responseBody, + Map headers) { + this.endpoint = endpoint + this.status = status + this.responseBody = responseBody + this.headers = headers + } + + int getStatus() { + return status + } + + byte[] getResponseBody() { + return responseBody + } + + HttpServerTest.ServerEndpoint getEndpoint() { + return endpoint + } + + void fillHeaders(HttpResponsePacket.Builder builder) { + for (Map.Entry header : headers.entrySet()) { + builder.header(header.getKey(), header.getValue()) + } + } + } + } +} + + diff --git a/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyFilterchainServerTestInstrumentation.java b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyFilterchainServerTestInstrumentation.java new file mode 100644 index 0000000000..b69a8c3930 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyFilterchainServerTestInstrumentation.java @@ -0,0 +1,40 @@ +/* + * 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 net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.google.auto.service.AutoService; +import io.opentelemetry.auto.test.base.HttpServerTestAdvice; +import io.opentelemetry.auto.tooling.Instrumenter; +import net.bytebuddy.agent.builder.AgentBuilder; + +@AutoService(Instrumenter.class) +public class GrizzlyFilterchainServerTestInstrumentation implements Instrumenter { + + @Override + public AgentBuilder instrument(final AgentBuilder agentBuilder) { + return agentBuilder + .type(named("org.glassfish.grizzly.http.HttpCodecFilter")) + .transform( + new AgentBuilder.Transformer.ForAdvice() + .advice( + named("handleRead") + .and( + takesArgument( + 0, named("org.glassfish.grizzly.filterchain.FilterChainContext"))), + HttpServerTestAdvice.ServerEntryAdvice.class.getName())); + } +} diff --git a/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyIOStrategyTest.groovy b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyIOStrategyTest.groovy new file mode 100644 index 0000000000..60fe605c08 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyIOStrategyTest.groovy @@ -0,0 +1,66 @@ +/* + * 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 org.glassfish.grizzly.IOStrategy +import org.glassfish.grizzly.http.server.HttpServer +import org.glassfish.grizzly.strategies.LeaderFollowerNIOStrategy +import org.glassfish.grizzly.strategies.SameThreadIOStrategy +import org.glassfish.grizzly.strategies.SimpleDynamicNIOStrategy +import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory +import org.glassfish.jersey.server.ResourceConfig + +abstract class GrizzlyIOStrategyTest extends GrizzlyTest { + + static { + System.setProperty("ota.integration.grizzly-http.enabled", "true") + } + + @Override + HttpServer startServer(int port) { + ResourceConfig rc = new ResourceConfig() + rc.register(SimpleExceptionMapper) + rc.register(ServiceResource) + def server = GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc) + server.getListener("grizzly").getTransport().setIOStrategy(strategy()) + // Default in NIOTransportBuilder is WorkerThreadIOStrategy, so don't need to retest that.s + return server + } + + abstract IOStrategy strategy() +} + +class LeaderFollowerTest extends GrizzlyIOStrategyTest { + + @Override + IOStrategy strategy() { + return LeaderFollowerNIOStrategy.instance + } +} + +class SameThreadTest extends GrizzlyIOStrategyTest { + + @Override + IOStrategy strategy() { + return SameThreadIOStrategy.instance + } +} + +class SimpleDynamicTest extends GrizzlyIOStrategyTest { + + @Override + IOStrategy strategy() { + return SimpleDynamicNIOStrategy.instance + } +} diff --git a/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyTest.groovy b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyTest.groovy new file mode 100644 index 0000000000..f4f72c2831 --- /dev/null +++ b/instrumentation/grizzly-http-2.3/src/test/groovy/GrizzlyTest.groovy @@ -0,0 +1,108 @@ +/* + * 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.opentelemetry.auto.test.base.HttpServerTest +import org.glassfish.grizzly.http.server.HttpServer +import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory +import org.glassfish.jersey.server.ResourceConfig + +import javax.ws.rs.GET +import javax.ws.rs.NotFoundException +import javax.ws.rs.Path +import javax.ws.rs.QueryParam +import javax.ws.rs.core.Response +import javax.ws.rs.ext.ExceptionMapper + +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.EXCEPTION +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.REDIRECT +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS + +class GrizzlyTest extends HttpServerTest { + + static { + System.setProperty("ota.integration.grizzly-filterchain.enabled", "true") + } + + @Override + HttpServer startServer(int port) { + ResourceConfig rc = new ResourceConfig() + rc.register(SimpleExceptionMapper) + rc.register(ServiceResource) + GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc) + } + + @Override + void stopServer(HttpServer server) { + server.stop() + } + + static class SimpleExceptionMapper implements ExceptionMapper { + + @Override + Response toResponse(Throwable exception) { + if (exception instanceof NotFoundException) { + return exception.getResponse() + } + Response.status(500).entity(exception.message).build() + } + } + + @Path("/") + static class ServiceResource { + + @GET + @Path("success") + Response success() { + controller(SUCCESS) { + Response.status(SUCCESS.status).entity(SUCCESS.body).build() + } + } + + @GET + @Path("query") + Response query_param(@QueryParam("some") String param) { + controller(QUERY_PARAM) { + Response.status(QUERY_PARAM.status).entity("some=$param".toString()).build() + } + } + + @GET + @Path("redirect") + Response redirect() { + controller(REDIRECT) { + Response.status(REDIRECT.status).location(new URI(REDIRECT.body)).build() + } + } + + @GET + @Path("error-status") + Response error() { + controller(ERROR) { + Response.status(ERROR.status).entity(ERROR.body).build() + } + } + + @GET + @Path("exception") + Response exception() { + controller(EXCEPTION) { + throw new Exception(EXCEPTION.body) + } + return null + } + } +} diff --git a/settings.gradle b/settings.gradle index da890be762..7f53272c98 100644 --- a/settings.gradle +++ b/settings.gradle @@ -65,6 +65,8 @@ include ':instrumentation:finatra-2.9' include ':instrumentation:geode-1.4' include ':instrumentation:google-http-client-1.19' include ':instrumentation:grizzly-2.0' +include ':instrumentation:grizzly-client-1.9' +include ':instrumentation:grizzly-http-2.3' include ':instrumentation:grpc-1.5' include ':instrumentation:hibernate:hibernate-3.3' include ':instrumentation:hibernate:hibernate-4.0' diff --git a/testing/src/main/groovy/io/opentelemetry/auto/test/base/HttpServerTest.groovy b/testing/src/main/groovy/io/opentelemetry/auto/test/base/HttpServerTest.groovy index 1826c21047..d157dcfa11 100644 --- a/testing/src/main/groovy/io/opentelemetry/auto/test/base/HttpServerTest.groovy +++ b/testing/src/main/groovy/io/opentelemetry/auto/test/base/HttpServerTest.groovy @@ -124,6 +124,10 @@ abstract class HttpServerTest extends AgentTestRunner { true } + boolean testException() { + true + } + enum ServerEndpoint { SUCCESS("success", 200, "success"), REDIRECT("redirect", 302, "/redirected"), @@ -304,6 +308,7 @@ abstract class HttpServerTest extends AgentTestRunner { def "test exception"() { setup: + assumeTrue(testException()) def request = request(EXCEPTION, method, body).build() def response = client.newCall(request).execute()