diff --git a/.github/workflows/plugins-test.1.yaml b/.github/workflows/plugins-test.1.yaml
index 0674b0b28f..ad17e7fefa 100644
--- a/.github/workflows/plugins-test.1.yaml
+++ b/.github/workflows/plugins-test.1.yaml
@@ -77,6 +77,7 @@ jobs:
- mssql-jtds-scenario
- mssql-jdbc-scenario
- mybatis-3.x-scenario
+ - resteasy-4.x-scenario
steps:
- uses: actions/checkout@v2
with:
diff --git a/CHANGES.md b/CHANGES.md
index 12e4f349b9..628c5c7eff 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,7 @@ Release Notes.
* Fix `Shenyu plugin`'s NPE in reading read trace ID when IgnoredTracerContext is used in the context.
* Update witness class in elasticsearch-6.x-plugin, avoid throw NPE.
* Fix `onHalfClose` using span operation name `/Request/onComplete` instead of the worng name `/Request/onHalfClose`.
+* Add plugin to support for RESTeasy 4.x.
#### Documentation
diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml
index dacb1d922b..5d6f8fd761 100644
--- a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml
@@ -27,6 +27,7 @@
resteasy-plugin
resteasy-server-3.x-plugin
+ resteasy-server-4.x-plugin
pom
diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/pom.xml
new file mode 100644
index 0000000000..eaf4f65ecd
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/pom.xml
@@ -0,0 +1,53 @@
+
+
+
+ 4.0.0
+
+ resteasy-plugin
+ org.apache.skywalking
+ 8.12.0-SNAPSHOT
+
+
+ resteasy-server-4.x-plugin
+ jar
+
+ apm-resteasy-server-4.x-plugin
+ http://maven.apache.org
+
+
+ 4.7.6.Final
+ 3.15.3.Final
+
+
+
+
+ org.jboss.resteasy
+ resteasy-core
+ ${resteasy.version}
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-jaxrs
+ ${resteasy-jaxrs.version}
+ provided
+
+
+
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/SynchronousDispatcherExceptionInterceptor.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/SynchronousDispatcherExceptionInterceptor.java
new file mode 100644
index 0000000000..57e0c6b815
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/SynchronousDispatcherExceptionInterceptor.java
@@ -0,0 +1,50 @@
+/*
+ * 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.v4.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;
+
+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().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().log(t);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/SynchronousDispatcherInterceptor.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/SynchronousDispatcherInterceptor.java
new file mode 100644
index 0000000000..231ffb8851
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/SynchronousDispatcherInterceptor.java
@@ -0,0 +1,88 @@
+/*
+ * 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.v4.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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Method;
+
+public class SynchronousDispatcherInterceptor implements InstanceMethodsAroundInterceptor {
+ private static final Logger LOG = LoggerFactory.getLogger(SynchronousDispatcherInterceptor.class);
+
+ @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()));
+ }
+
+ String operationName = request.getHttpMethod() + ":" + request.getUri().getPath();
+ AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier);
+ span.tag(Tags.URL, toPath(request.getUri().getRequestUri().toString()));
+ span.tag(Tags.HTTP.METHOD, 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.HTTP_RESPONSE_STATUS_CODE.set(span, response.getStatus());
+ ContextManager.stopSpan();
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
+ Class>[] argumentsTypes, Throwable t) {
+ ContextManager.activeSpan().log(t);
+ }
+
+ private static String toPath(String uri) {
+ int index = uri.indexOf("?");
+ if (index > -1) {
+ return uri.substring(0, index);
+ } else {
+ return uri;
+ }
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/define/SynchronousDispatcherInstrumentation.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/define/SynchronousDispatcherInstrumentation.java
new file mode 100644
index 0000000000..245994dc69
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/define/SynchronousDispatcherInstrumentation.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.v4.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;
+
+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.v4.server.SynchronousDispatcherInterceptor";
+ private static final String INVOKE_EXCEPTION_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.resteasy.v4.server.SynchronousDispatcherExceptionInterceptor";
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return null;
+ }
+
+ @Override
+ public 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-4.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 0000000000..dd7cfe0aac
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.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-4.x=org.apache.skywalking.apm.plugin.resteasy.v4.server.define.SynchronousDispatcherInstrumentation
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/AssertTools.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/AssertTools.java
new file mode 100644
index 0000000000..991ead2a12
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/AssertTools.java
@@ -0,0 +1,47 @@
+/*
+ * 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.v4.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;
+
+class AssertTools {
+
+ static void assertTraceSegmentRef(TraceSegmentRef ref) {
+ assertThat(SegmentRefHelper.getParentServiceInstance(ref), is("instance"));
+ assertThat(SegmentRefHelper.getSpanId(ref), is(3));
+ assertThat(SegmentRefHelper.getTraceSegmentId(ref).toString(), is("3.4.5"));
+ }
+
+ static void assertHttpSpan(AbstractTracingSpan span) {
+ assertThat(span.getOperationName(), is("GET:/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-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/SynchronousDispatcherInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/SynchronousDispatcherInterceptorTest.java
new file mode 100644
index 0000000000..df54c505e0
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/SynchronousDispatcherInterceptorTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.v4.server;
+
+import org.apache.skywalking.apm.agent.core.context.SW8CarrierItem;
+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.AgentServiceRule;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStorage;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint;
+import org.apache.skywalking.apm.agent.test.tools.SpanAssert;
+import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+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;
+
+@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(request.getHttpMethod()).thenReturn("GET");
+ 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(SW8CarrierItem.HEADER_NAME, "1-My40LjU=-MS4yLjM=-3-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA=");
+ 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.getRef());
+ }
+
+ @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/Plugin-list.md b/docs/en/setup/service-agent/java-agent/Plugin-list.md
index d5d04c2dda..7647f0a4d5 100644
--- a/docs/en/setup/service-agent/java-agent/Plugin-list.md
+++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md
@@ -74,6 +74,7 @@
- rabbitmq-5.x
- redisson-3.x
- resteasy-server-3.x
+- resteasy-server-4.x
- rocketMQ-3.x
- rocketMQ-4.x
- sentinel-1.x
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 66d8d77f60..f566b9ad7e 100644
--- a/docs/en/setup/service-agent/java-agent/Supported-list.md
+++ b/docs/en/setup/service-agent/java-agent/Supported-list.md
@@ -15,7 +15,7 @@ metrics based on the tracing data.
* [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 (Optional¹)
* [Undertow](http://undertow.io/) 1.3.0.Final -> 2.0.27.Final
- * [RESTEasy](https://resteasy.github.io/) 3.1.0.Final -> 3.7.0.Final
+ * [RESTEasy](https://resteasy.github.io/) 3.1.0.Final -> 4.7.6.Final
* [Play Framework](https://www.playframework.com/) 2.6.x -> 2.8.x
* [Light4J Microservices Framework](https://doc.networknt.com/) 1.6.x -> 2.x
* [Netty SocketIO](https://github.com/mrniko/netty-socketio) 1.x
diff --git a/pom.xml b/pom.xml
index 60974d15b4..27d80100ac 100755
--- a/pom.xml
+++ b/pom.xml
@@ -391,6 +391,7 @@
${project.build.sourceDirectory}
${project.build.testSourceDirectory}
scenarios/okhttp-scenario
+scenarios/spring-4.3.x-scenario
**/*.properties,
diff --git a/test/plugin/scenarios/resteasy-4.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/resteasy-4.x-scenario/config/expectedData.yaml
new file mode 100644
index 0000000000..08dd31dfa5
--- /dev/null
+++ b/test/plugin/scenarios/resteasy-4.x-scenario/config/expectedData.yaml
@@ -0,0 +1,190 @@
+# 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.
+segmentItems:
+ - serviceName: resteasy-4.x-scenario
+ segmentSize: ge 6
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: HEAD:/resteasy-4.x-scenario/healthCheck
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 1
+ isError: false
+ spanType: Entry
+ peer: ''
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/healthCheck' }
+ - { key: http.method, value: HEAD }
+ skipAnalysis: 'false'
+ - segmentId: not null
+ spans:
+ - operationName: POST:/resteasy-4.x-scenario/create/
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 1
+ isError: false
+ spanType: Entry
+ peer: ''
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/create/' }
+ - { key: http.method, value: POST }
+ refs:
+ - { parentEndpoint: 'GET:/resteasy-4.x-scenario/case/resttemplate', networkAddress: 'localhost:8080', refType: CrossProcess,
+ parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not
+ null, parentService: resteasy-4.x-scenario, traceId: not null }
+ skipAnalysis: 'false'
+ - segmentId: not null
+ spans:
+ - operationName: GET:/resteasy-4.x-scenario/get/1
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 1
+ isError: false
+ spanType: Entry
+ peer: ''
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/get/1' }
+ - { key: http.method, value: GET }
+ refs:
+ - { parentEndpoint: 'GET:/resteasy-4.x-scenario/case/resttemplate', networkAddress: 'localhost:8080', refType: CrossProcess,
+ parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not
+ null, parentService: resteasy-4.x-scenario, traceId: not null }
+ skipAnalysis: 'false'
+ - segmentId: not null
+ spans:
+ - operationName: PUT:/resteasy-4.x-scenario/update/1
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 1
+ isError: false
+ spanType: Entry
+ peer: ''
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/update/1' }
+ - { key: http.method, value: PUT }
+ refs:
+ - { parentEndpoint: 'GET:/resteasy-4.x-scenario/case/resttemplate', networkAddress: 'localhost:8080', refType: CrossProcess,
+ parentSpanId: 3, parentTraceSegmentId: not null, parentServiceInstance: not
+ null, parentService: resteasy-4.x-scenario, traceId: not null }
+ skipAnalysis: 'false'
+ - segmentId: not null
+ spans:
+ - operationName: DELETE:/resteasy-4.x-scenario/delete/1
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 1
+ isError: false
+ spanType: Entry
+ peer: ''
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/delete/1' }
+ - { key: http.method, value: DELETE }
+ refs:
+ - { parentEndpoint: 'GET:/resteasy-4.x-scenario/case/resttemplate', networkAddress: 'localhost:8080', refType: CrossProcess,
+ parentSpanId: 4, parentTraceSegmentId: not null, parentServiceInstance: not
+ null, parentService: resteasy-4.x-scenario, traceId: not null }
+ skipAnalysis: 'false'
+ - segmentId: not null
+ spans:
+ - operationName: /resteasy-4.x-scenario/create/
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 2
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/create/' }
+ - { key: http.method, value: POST }
+ skipAnalysis: 'false'
+ - operationName: /resteasy-4.x-scenario/get/1
+ parentSpanId: 0
+ spanId: 2
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 2
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/get/1' }
+ - { key: http.method, value: GET }
+ skipAnalysis: 'false'
+ - operationName: /resteasy-4.x-scenario/update/1
+ parentSpanId: 0
+ spanId: 3
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 2
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/update/1' }
+ - { key: http.method, value: PUT }
+ skipAnalysis: 'false'
+ - operationName: /resteasy-4.x-scenario/delete/1
+ parentSpanId: 0
+ spanId: 4
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 2
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/delete/1' }
+ - { key: http.method, value: DELETE }
+ skipAnalysis: 'false'
+ - operationName: GET:/resteasy-4.x-scenario/case/resttemplate
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 1
+ isError: false
+ spanType: Entry
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - { key: url, value: 'http://localhost:8080/resteasy-4.x-scenario/case/resttemplate' }
+ - { key: http.method, value: GET }
\ No newline at end of file
diff --git a/test/plugin/scenarios/resteasy-4.x-scenario/configuration.yml b/test/plugin/scenarios/resteasy-4.x-scenario/configuration.yml
new file mode 100644
index 0000000000..f17c8b9661
--- /dev/null
+++ b/test/plugin/scenarios/resteasy-4.x-scenario/configuration.yml
@@ -0,0 +1,21 @@
+# 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.
+
+type: tomcat
+entryService: http://localhost:8080/resteasy-4.x-scenario/case/resttemplate
+healthCheck: http://localhost:8080/resteasy-4.x-scenario/healthCheck
+environment:
+ - CATALINA_OPTS="-Dskywalking.plugin.http.include_http_headers=mock_header"
\ No newline at end of file
diff --git a/test/plugin/scenarios/resteasy-4.x-scenario/pom.xml b/test/plugin/scenarios/resteasy-4.x-scenario/pom.xml
new file mode 100644
index 0000000000..a0b86fa663
--- /dev/null
+++ b/test/plugin/scenarios/resteasy-4.x-scenario/pom.xml
@@ -0,0 +1,96 @@
+
+
+
+ 4.0.0
+
+ org.apache.skywalking
+ resteasy-4.x-scenario
+ 1.0.0
+ war
+
+
+ UTF-8
+ 1.8
+ 4.7.6.Final
+ 2.8.1
+
+
+
+
+
+ org.jboss.resteasy
+ resteasy-core
+ ${test.framework.version}
+
+
+
+ org.jboss.resteasy
+ resteasy-jaxb-provider
+ ${test.framework.version}
+
+
+
+ org.jboss.resteasy
+ resteasy-servlet-initializer
+ ${test.framework.version}
+
+
+
+
+ org.jboss.resteasy
+ resteasy-jackson2-provider
+ ${test.framework.version}
+
+
+
+ org.jboss.resteasy
+ resteasy-client
+ ${test.framework.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ ${log4j.version}
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j.version}
+
+
+
+
+
+ resteasy-4.x-scenario
+
+
+ maven-compiler-plugin
+
+ ${compiler.version}
+ ${compiler.version}
+ ${project.build.sourceEncoding}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/plugin/scenarios/resteasy-4.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/RestTemplateController.java b/test/plugin/scenarios/resteasy-4.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/RestTemplateController.java
new file mode 100644
index 0000000000..fc0d33d555
--- /dev/null
+++ b/test/plugin/scenarios/resteasy-4.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/RestTemplateController.java
@@ -0,0 +1,74 @@
+/*
+ * 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 test.apache.skywalking.apm.testcase;
+
+import test.apache.skywalking.apm.testcase.entity.User;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+@Path("/")
+public class RestTemplateController {
+
+ private static final String SUCCESS = "Success";
+
+ private static final String URL = "http://localhost:8080/resteasy-4.x-scenario";
+
+ @GET()
+ @Path("/case/resttemplate")
+ public Response restTemplate() {
+ Client client = ClientBuilder.newBuilder().build();
+
+ // Create user
+ User userEntity = new User(1, "a");
+ WebTarget target = client.target(URL + "/create/");
+ Response response = target.request().post(Entity.json(userEntity));
+ String value = response.readEntity(String.class);
+ response.close();
+
+ // Find User
+ response = client.target(URL + "/get/1").request().get();
+ response.close();
+
+ //Modify user
+ User updateUserEntity = new User(1, "b");
+ response = client.target(URL + "/update/1").request().put(Entity.json(updateUserEntity));
+ response.close();
+
+ //Delete user
+ response = client.target(URL + "/delete/1").request().delete();
+ response.close();
+
+ client.close();
+
+ return Response.ok(SUCCESS).build();
+ }
+
+ @GET
+ @Path("/healthCheck")
+ public String healthCheck() {
+ return SUCCESS;
+ }
+
+}
\ No newline at end of file
diff --git a/test/plugin/scenarios/resteasy-4.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/UserApplication.java b/test/plugin/scenarios/resteasy-4.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/UserApplication.java
new file mode 100644
index 0000000000..fbff4e2f5a
--- /dev/null
+++ b/test/plugin/scenarios/resteasy-4.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/UserApplication.java
@@ -0,0 +1,45 @@
+/*
+ * 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 test.apache.skywalking.apm.testcase;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+import java.util.HashSet;
+import java.util.Set;
+
+@ApplicationPath("/")
+public class UserApplication extends Application {
+ private Set