From bc6f06f964e42d1321bf9ed0c3cf355973a9a983 Mon Sep 17 00:00:00 2001 From: "FuCheng,Yan" Date: Fri, 24 May 2019 22:14:43 +0800 Subject: [PATCH] Support RESTEasy 3.x (#2665) * feat(agent sdk): resteasy plugin * fix(agent sdk): fix component define * fix(agent sdk): fix file license * fix(agent sdk): fix file license * docs(agent sdk): add supported list * fix(agent sdk): fix file license * fix(agent sdk): fix response error tag * fix(agent sdk): fix * fix(agent sdk): adapter 3.5+ * fix(agent sdk): remove unreasonable async handling --- .../trace/component/ComponentsDefine.java | 3 + apm-sniffer/apm-sdk-plugin/pom.xml | 1 + .../apm-sdk-plugin/resteasy-plugin/pom.xml | 43 ++++ .../resteasy-server-3.x-plugin/pom.xml | 47 +++++ ...hronousDispatcherExceptionInterceptor.java | 53 +++++ .../SynchronousDispatcherInterceptor.java | 86 ++++++++ .../SynchronousDispatcherInstrumentation.java | 89 ++++++++ .../src/main/resources/skywalking-plugin.def | 17 ++ .../resteasy/v3/server/AssertTools.java | 49 +++++ .../SynchronousDispatcherInterceptorTest.java | 192 ++++++++++++++++++ .../java-agent/Supported-list.md | 1 + .../test/resources/component-libraries.yml | 3 + .../main/resources/component-libraries.yml | 3 + 13 files changed, 587 insertions(+) create mode 100644 apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml create mode 100644 apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/pom.xml create mode 100644 apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherExceptionInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/define/SynchronousDispatcherInstrumentation.java create mode 100644 apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/resources/skywalking-plugin.def create mode 100644 apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/AssertTools.java create mode 100644 apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherInterceptorTest.java diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java index c3a10f28f7..38f9dcad14 100644 --- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java +++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java @@ -120,6 +120,8 @@ public class ComponentsDefine { public static final OfficialComponent SPRING_CLOUD_GATEWAY = new OfficialComponent(61, "spring-cloud-gateway"); + public static final OfficialComponent RESTEASY = new OfficialComponent(62, "RESTEasy"); + private static ComponentsDefine INSTANCE = new ComponentsDefine(); private String[] components; @@ -176,6 +178,7 @@ public class ComponentsDefine { addComponent(ZOOKEEPER); addComponent(VERTX); addComponent(SPRING_CLOUD_GATEWAY); + addComponent(RESTEASY); } private void addComponent(OfficialComponent component) { diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml index 1bd9a70cdc..0060c880f0 100644 --- a/apm-sniffer/apm-sdk-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pom.xml @@ -71,6 +71,7 @@ dubbo-2.7.x-plugin dubbo-2.7.x-conflict-patch vertx-plugins + resteasy-plugin pom diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml new file mode 100644 index 0000000000..75862ebccc --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + org.apache.skywalking + apm-sdk-plugin + 6.2.0-SNAPSHOT + + + resteasy-plugin + + resteasy-server-3.x-plugin + + + pom + + resteasy-plugin + http://maven.apache.org + + + UTF-8 + /.. + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/pom.xml new file mode 100644 index 0000000000..94756fd658 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + resteasy-plugin + org.apache.skywalking + 6.2.0-SNAPSHOT + + + resteasy-server-3.x-plugin + jar + + apm-resteasy-server-3.x-plugin + http://maven.apache.org + + + 3.1.0.Final + + + + + org.jboss.resteasy + resteasy-jaxrs + ${resteasy.version} + provided + + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherExceptionInterceptor.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherExceptionInterceptor.java new file mode 100644 index 0000000000..5938716279 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherExceptionInterceptor.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.resteasy.v3.server; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.jboss.resteasy.spi.HttpRequest; + +import java.lang.reflect.Method; + +/** + * @author yan-fucheng + */ +public class SynchronousDispatcherExceptionInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + if (ContextManager.isActive() && !((HttpRequest) allArguments[0]).getAsyncContext().isSuspended()) { + ContextManager.activeSpan().errorOccurred().log((Throwable)allArguments[2]); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().errorOccurred().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherInterceptor.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherInterceptor.java new file mode 100644 index 0000000000..eec9673952 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherInterceptor.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.resteasy.v3.server; + +import org.apache.skywalking.apm.agent.core.context.CarrierItem; +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; + +import java.lang.reflect.Method; + +/** + * @author yan-fucheng + */ +public class SynchronousDispatcherInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + HttpRequest request = (HttpRequest) allArguments[0]; + + ContextCarrier contextCarrier = new ContextCarrier(); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + next.setHeadValue(request.getHttpHeaders().getHeaderString(next.getHeadKey())); + } + + AbstractSpan span = ContextManager.createEntrySpan(request.getUri().getPath(), contextCarrier); + Tags.URL.set(span, toPath(request.getUri().getRequestUri().toString())); + Tags.HTTP.METHOD.set(span, request.getHttpMethod()); + span.setComponent(ComponentsDefine.RESTEASY); + SpanLayer.asHttp(span); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + HttpResponse response = (HttpResponse) allArguments[1]; + AbstractSpan span = ContextManager.activeSpan(); + if (response.getStatus() >= 400) { + span.errorOccurred(); + Tags.STATUS_CODE.set(span, Integer.toString(response.getStatus())); + } + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().errorOccurred().log(t); + } + + private static String toPath(String uri) { + if (uri.contains("?")) { + return uri.substring(0, uri.indexOf("?")); + } else { + return uri; + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/define/SynchronousDispatcherInstrumentation.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/define/SynchronousDispatcherInstrumentation.java new file mode 100644 index 0000000000..f9678b476a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/define/SynchronousDispatcherInstrumentation.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.resteasy.v3.server.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +/** + * @author yan-fucheng + */ +public class SynchronousDispatcherInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.jboss.resteasy.core.SynchronousDispatcher"; + + private static final String INVOKE_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.resteasy.v3.server.SynchronousDispatcherInterceptor"; + private static final String INVOKE_EXCEPTION_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.resteasy.v3.server.SynchronousDispatcherExceptionInterceptor"; + + @Override + protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return null; + } + + @Override + protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("invoke").and(takesArguments(3)); + } + + @Override + public String getMethodsInterceptor() { + return INVOKE_INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("writeException").and(takesArguments(3)); + } + + @Override + public String getMethodsInterceptor() { + return INVOKE_EXCEPTION_INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..164c991bc2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +resteasy-server-3.x=org.apache.skywalking.apm.plugin.resteasy.v3.server.define.SynchronousDispatcherInstrumentation \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/AssertTools.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/AssertTools.java new file mode 100644 index 0000000000..20b2f65ea7 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/AssertTools.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.resteasy.v3.server; + +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegmentRef; +import org.apache.skywalking.apm.agent.test.helper.SegmentRefHelper; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +import static org.apache.skywalking.apm.agent.test.tools.SpanAssert.assertComponent; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author yan-fucheng + */ +class AssertTools { + + static void assertTraceSegmentRef(TraceSegmentRef ref) { + assertThat(SegmentRefHelper.getEntryServiceInstanceId(ref), is(1)); + assertThat(SegmentRefHelper.getSpanId(ref), is(3)); + assertThat(SegmentRefHelper.getTraceSegmentId(ref).toString(), is("1.234.111")); + } + + static void assertHttpSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), is("/test/testRequestURL")); + assertComponent(span, ComponentsDefine.RESTEASY); + SpanAssert.assertTag(span, 0, "http://localhost:8080/test/testRequestURL"); + assertThat(span.isEntry(), is(true)); + SpanAssert.assertLayer(span, SpanLayer.HTTP); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherInterceptorTest.java new file mode 100644 index 0000000000..e583e0f4f7 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/SynchronousDispatcherInterceptorTest.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.resteasy.v3.server; + +import org.apache.skywalking.apm.agent.core.conf.Config; +import org.apache.skywalking.apm.agent.core.context.SW3CarrierItem; +import org.apache.skywalking.apm.agent.core.context.SW6CarrierItem; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.LogDataEntity; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.helper.SpanHelper; +import org.apache.skywalking.apm.agent.test.tools.*; +import org.jboss.resteasy.core.ResourceInvoker; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; +import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; +import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; +import org.jboss.resteasy.spi.ResteasyAsynchronousContext; +import org.jboss.resteasy.spi.ResteasyUriInfo; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.PowerMockRunnerDelegate; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.when; + +/** + * @author yan-fucheng + */ +@RunWith(PowerMockRunner.class) +@PowerMockRunnerDelegate(TracingSegmentRunner.class) +public class SynchronousDispatcherInterceptorTest { + + private SynchronousDispatcherInterceptor synchronousDispatcherInterceptor; + private SynchronousDispatcherExceptionInterceptor exceptionInterceptor; + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + + @Mock + HttpRequest request; + + @Mock + HttpResponse response; + + @Mock + ResourceInvoker resourceInvoker; + + @Mock + private MethodInterceptResult methodInterceptResult; + + @Mock + private ResteasyAsynchronousContext resteasyAsynchronousContext; + + @Mock + EnhancedInstance enhancedInstance; + + private Object[] arguments; + private Class[] argumentType; + + private Object[] exceptionArguments; + private Class[] exceptionArgumentType; + + @Before + public void setup() throws URISyntaxException { + synchronousDispatcherInterceptor = new SynchronousDispatcherInterceptor(); + exceptionInterceptor = new SynchronousDispatcherExceptionInterceptor(); + when(request.getUri()).thenReturn(new ResteasyUriInfo(new URI("http://localhost:8080/test/testRequestURL"))); + when(request.getHttpHeaders()).thenReturn(new ResteasyHttpHeaders(new MultivaluedMapImpl())); + when(response.getStatus()).thenReturn(200); + when(request.getAsyncContext()).thenReturn(resteasyAsynchronousContext); + when(request.getAsyncContext().isSuspended()).thenReturn(false); + arguments = new Object[] {request, response, resourceInvoker}; + argumentType = new Class[] {request.getClass(), response.getClass(), resourceInvoker.getClass()}; + + exceptionArguments = new Object[] {request, response, new RuntimeException()}; + exceptionArgumentType = new Class[] {request.getClass(), response.getClass(), new RuntimeException().getClass()}; + } + + @Test + public void testWithoutSerializedContextData() throws Throwable { + synchronousDispatcherInterceptor.beforeMethod(enhancedInstance, null, arguments, argumentType, methodInterceptResult); + synchronousDispatcherInterceptor.afterMethod(enhancedInstance, null, arguments, argumentType, null); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + AssertTools.assertHttpSpan(spans.get(0)); + } + + @Test + public void testWithSW6SerializedContextData() throws Throwable { + MultivaluedMapImpl multivaluedMap = new MultivaluedMapImpl(); + multivaluedMap.putSingle(SW6CarrierItem.HEADER_NAME, "1-I0FRQSojQVFBKkV0MFdlMHRRTlFBKg==-MS4yMzQuMTEx-3-1-1-IzE5Mi4xNjguMS44OjE4MDAy-Iy9wb3J0YWwv-Iy90ZXN0RW50cnlTcGFu"); + when(request.getHttpHeaders()).thenReturn(new ResteasyHttpHeaders(multivaluedMap)); + + synchronousDispatcherInterceptor.beforeMethod(enhancedInstance, null, arguments, argumentType, methodInterceptResult); + synchronousDispatcherInterceptor.afterMethod(enhancedInstance, null, arguments, argumentType, null); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + AssertTools.assertHttpSpan(spans.get(0)); + AssertTools.assertTraceSegmentRef(traceSegment.getRefs().get(0)); + } + + @Test + public void testWithSW3SerializedContextData() throws Throwable { + Config.Agent.ACTIVE_V1_HEADER = true; + Config.Agent.ACTIVE_V2_HEADER = false; + MultivaluedMapImpl multivaluedMap = new MultivaluedMapImpl(); + multivaluedMap.putSingle(SW3CarrierItem.HEADER_NAME, "1.234.111|3|1|1|#192.168.1.8:18002|#/portal/|#/testEntrySpan|#AQA*#AQA*Et0We0tQNQA*"); + when(request.getHttpHeaders()).thenReturn(new ResteasyHttpHeaders(multivaluedMap)); + + synchronousDispatcherInterceptor.beforeMethod(enhancedInstance, null, arguments, argumentType, methodInterceptResult); + synchronousDispatcherInterceptor.afterMethod(enhancedInstance, null, arguments, argumentType, null); + + Config.Agent.ACTIVE_V1_HEADER = false; + Config.Agent.ACTIVE_V2_HEADER = true; + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + AssertTools.assertHttpSpan(spans.get(0)); + AssertTools.assertTraceSegmentRef(traceSegment.getRefs().get(0)); + } + + @Test + public void testWithOccurException() throws Throwable { + synchronousDispatcherInterceptor.beforeMethod(enhancedInstance, null, arguments, argumentType, methodInterceptResult); + synchronousDispatcherInterceptor.handleMethodException(enhancedInstance, null, arguments, argumentType, new RuntimeException()); + synchronousDispatcherInterceptor.afterMethod(enhancedInstance, null, arguments, argumentType, null); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + AssertTools.assertHttpSpan(spans.get(0)); + List logDataEntities = SpanHelper.getLogs(spans.get(0)); + assertThat(logDataEntities.size(), is(1)); + SpanAssert.assertException(logDataEntities.get(0), RuntimeException.class); + } + + @Test + public void testWithMainThreadOccurException() throws Throwable { + synchronousDispatcherInterceptor.beforeMethod(enhancedInstance, null, arguments, argumentType, methodInterceptResult); + exceptionInterceptor.beforeMethod(enhancedInstance, null, exceptionArguments, exceptionArgumentType, null); + synchronousDispatcherInterceptor.afterMethod(enhancedInstance, null, arguments, argumentType, null); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + AssertTools.assertHttpSpan(spans.get(0)); + List logDataEntities = SpanHelper.getLogs(spans.get(0)); + assertThat(logDataEntities.size(), is(1)); + SpanAssert.assertException(logDataEntities.get(0), RuntimeException.class); + } +} diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md index 6b0f8064ba..83734888be 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -11,6 +11,7 @@ * [Jetty Server](http://www.eclipse.org/jetty/) 9 * [Spring Webflux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) 5.x * [Undertow](http://undertow.io/) 2.0.0.Final -> 2.0.13.Final + * [RESTEasy](https://resteasy.github.io/) 3.1.0.Final -> 3.7.0.Final * HTTP Client * [Feign](https://github.com/OpenFeign/feign) 9.x * [Netflix Spring Cloud Feign](https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-starter-feign) 1.1.x, 1.2.x, 1.3.x diff --git a/oap-server/server-core/src/test/resources/component-libraries.yml b/oap-server/server-core/src/test/resources/component-libraries.yml index 3b5f49da53..97733d8026 100644 --- a/oap-server/server-core/src/test/resources/component-libraries.yml +++ b/oap-server/server-core/src/test/resources/component-libraries.yml @@ -195,6 +195,9 @@ ShardingSphere: spring-cloud-gateway: id: 61 languages: Java +RESTEasy: + id: 62 + languages: Java # .NET/.NET Core components # [3000, 4000) for C#/.NET only diff --git a/oap-server/server-starter/src/main/resources/component-libraries.yml b/oap-server/server-starter/src/main/resources/component-libraries.yml index 7dc8242b64..4d9598842b 100644 --- a/oap-server/server-starter/src/main/resources/component-libraries.yml +++ b/oap-server/server-starter/src/main/resources/component-libraries.yml @@ -213,6 +213,9 @@ ShardingSphere: spring-cloud-gateway: id: 61 languages: Java +RESTEasy: + id: 62 + languages: Java # .NET/.NET Core components # [3000, 4000) for C#/.NET only -- GitLab