diff --git a/.travis.yml b/.travis.yml
index 69492c5060dcf6c8ed3e39923507bc2fd3f363da..9b51e79833f8843894d04b8bffa575a75c14ce00 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,6 +10,7 @@ install:
- mvn install:install-file -Dfile=jmxri-1.2.1.jar -DgroupId=com.sun.jmx -DartifactId=jmxri -Dversion=1.2.1 -Dpackaging=jar
- mvn install:install-file -Dfile=dubbox-2.8.4.jar -DgroupId=com.alibaba -DartifactId=dubbox -Dversion=2.8.4 -Dpackaging=jar
- mvn install:install-file -Dfile=ojdbc14-10.2.0.4.0.jar -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=10.2.0.4.0 -Dpackaging=jar
+ - mvn install:install-file -Dfile=resin-4.0.41.jar -DgroupId=com.caucho -DartifactId=resin -Dversion=4.0.41 -Dpackaging=jar
- cd ..
script:
diff --git a/apm-sniffer/apm-agent/pom.xml b/apm-sniffer/apm-agent/pom.xml
index bfc577a9e3febc318ee643c2256364ed3b8fa382..b33a84f8b92d001100860319e4d1a6e81b8c5410 100644
--- a/apm-sniffer/apm-agent/pom.xml
+++ b/apm-sniffer/apm-agent/pom.xml
@@ -60,6 +60,16 @@
apm-mongodb-3.x-plugin
${project.version}
+
+ org.skywalking
+ apm-resin-3.x-plugin
+ ${project.version}
+
+
+ org.skywalking
+ apm-resin-4.x-plugin
+ ${project.version}
+
diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml
index accc48abbd6d4e3ecefd2fd561aecd37357828ff..75d35f783c861fe39b80703e36305ccef69cffbb 100644
--- a/apm-sniffer/apm-sdk-plugin/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/pom.xml
@@ -18,6 +18,8 @@
tomcat-7.x-8.x-plugin
motan-plugin
mongodb-3.x-plugin
+ resin-3.x-plugin
+ resin-4.x-plugin
pom
diff --git a/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2ae8dc3db535ea2d5c5f51c84069178829d98407
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/pom.xml
@@ -0,0 +1,56 @@
+
+
+ 4.0.0
+
+
+ apm-sdk-plugin
+ org.skywalking
+ 3.1-2017
+
+
+ apm-resin-3.x-plugin
+ jar
+
+ resin-3.x-plugin
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+ com.caucho
+ resin
+ 3.0.9
+ provided
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.0.1
+ provided
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/main/java/org/skywalking/apm/plugin/resin/v3/ResinV3Interceptor.java b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/main/java/org/skywalking/apm/plugin/resin/v3/ResinV3Interceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..c09372faf1237942453bb9572163bf0157e06478
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/main/java/org/skywalking/apm/plugin/resin/v3/ResinV3Interceptor.java
@@ -0,0 +1,72 @@
+package org.skywalking.apm.plugin.resin.v3;
+
+import com.caucho.server.http.HttpResponse;
+import com.caucho.server.http.HttpRequest;
+import org.skywalking.apm.agent.core.context.ContextCarrier;
+import org.skywalking.apm.agent.core.context.ContextManager;
+import org.skywalking.apm.agent.core.plugin.interceptor.EnhancedClassInstanceContext;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodInvokeContext;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.skywalking.apm.trace.Span;
+import org.skywalking.apm.trace.tag.Tags;
+import org.skywalking.apm.util.StringUtil;
+
+/**
+ * {@link ResinV3Interceptor} intercept method of{@link com.caucho.server.dispatch.ServletInvocation#service(javax.servlet.ServletRequest,
+ * javax.servlet.ServletResponse)} record the resin host, port ,url.
+ *
+ * @author baiyang
+ */
+public class ResinV3Interceptor implements InstanceMethodsAroundInterceptor {
+ /**
+ * Header name that the serialized context data stored in
+ * {@link HttpRequest#getHeader(String)}.
+ */
+ public static final String HEADER_NAME_OF_CONTEXT_DATA = "SWTraceContext";
+ /**
+ * Resin component.
+ */
+ public static final String RESIN_COMPONENT = "Resin";
+
+ @Override
+ public void beforeMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext,
+ MethodInterceptResult result) {
+ Object[] args = interceptorContext.allArguments();
+ HttpRequest request = (HttpRequest)args[0];
+ Span span = ContextManager.createSpan(request.getRequestURI());
+ Tags.COMPONENT.set(span, RESIN_COMPONENT);
+ Tags.PEER_HOST.set(span, request.getServerName());
+ Tags.PEER_PORT.set(span, request.getServerPort());
+ Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_SERVER);
+ Tags.URL.set(span, request.getRequestURL().toString());
+ Tags.SPAN_LAYER.asHttp(span);
+
+ String tracingHeaderValue = request.getHeader(HEADER_NAME_OF_CONTEXT_DATA);
+ if (!StringUtil.isEmpty(tracingHeaderValue)) {
+ ContextManager.extract(new ContextCarrier().deserialize(tracingHeaderValue));
+ }
+ }
+
+ @Override
+ public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext,
+ Object ret) {
+ HttpResponse response = (HttpResponse)interceptorContext.allArguments()[1];
+ Span span = ContextManager.activeSpan();
+ Tags.STATUS_CODE.set(span, response.getStatusCode());
+
+ if (response.getStatusCode() != 200) {
+ Tags.ERROR.set(span, true);
+ }
+ ContextManager.stopSpan();
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(Throwable t, EnhancedClassInstanceContext context,
+ InstanceMethodInvokeContext interceptorContext) {
+ Span span = ContextManager.activeSpan();
+ span.log(t);
+ Tags.ERROR.set(span, true);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/main/java/org/skywalking/apm/plugin/resin/v3/define/ResinV3Instrumentation.java b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/main/java/org/skywalking/apm/plugin/resin/v3/define/ResinV3Instrumentation.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b1369ef1efcf91d22003acd065bd80cc2950a83
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/main/java/org/skywalking/apm/plugin/resin/v3/define/ResinV3Instrumentation.java
@@ -0,0 +1,55 @@
+package org.skywalking.apm.plugin.resin.v3.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import org.skywalking.apm.plugin.resin.v3.ResinV3Interceptor;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+/**
+ * {@link ResinV3Instrumentation} presents that skywalking intercepts {@link com.caucho.server.dispatch.ServletInvocation#service(javax.servlet.ServletRequest,
+ * javax.servlet.ServletResponse)} by using {@link ResinV3Interceptor}.
+ *
+ * @author baiyang
+ */
+public class ResinV3Instrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "com.caucho.server.dispatch.ServletInvocation";
+
+ private static final String METHOD_INTERCET_CLASS = "org.skywalking.apm.plugin.resin.v3.ResinV3Interceptor";
+
+ @Override
+ protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return null;
+ }
+
+ @Override
+ protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[] {
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("service");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return METHOD_INTERCET_CLASS;
+ }
+ }
+ };
+ }
+
+ @Override
+ protected String enhanceClassName() {
+ return ENHANCE_CLASS;
+ }
+
+ @Override
+ protected String[] witnessClasses() {
+ return new String[] {"com.caucho.server.connection.AbstractHttpResponse"};
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 0000000000000000000000000000000000000000..86586edb85fcdd38390457b3e5bb5b22508c319d
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1 @@
+resin-3.x=org.skywalking.apm.plugin.resin.v3.define.ResinV3Instrumentation
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/test/java/org/skywalking/apm/plugin/resin/v3/ResinV3InterceptorTest.java b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/test/java/org/skywalking/apm/plugin/resin/v3/ResinV3InterceptorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..35e61448de7d3ac28e214359a5f232a9b65f2f1d
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/resin-3.x-plugin/src/test/java/org/skywalking/apm/plugin/resin/v3/ResinV3InterceptorTest.java
@@ -0,0 +1,147 @@
+package org.skywalking.apm.plugin.resin.v3;
+
+import com.caucho.server.http.HttpResponse;
+import com.caucho.server.http.HttpRequest;
+import org.hamcrest.CoreMatchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.skywalking.apm.agent.core.boot.ServiceManager;
+import org.skywalking.apm.agent.core.context.TracerContext;
+import org.skywalking.apm.agent.core.plugin.interceptor.EnhancedClassInstanceContext;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodInvokeContext;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.skywalking.apm.sniffer.mock.context.MockTracerContextListener;
+import org.skywalking.apm.sniffer.mock.context.SegmentAssert;
+import org.skywalking.apm.trace.LogData;
+import org.skywalking.apm.trace.Span;
+import org.skywalking.apm.trace.TraceSegment;
+import org.skywalking.apm.trace.TraceSegmentRef;
+import org.skywalking.apm.trace.tag.Tags;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+/**
+ * ResinInterceptorTest
+ *
+ * @author baiyang
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ResinV3InterceptorTest {
+ private ResinV3Interceptor interceptor;
+ private MockTracerContextListener contextListener;
+
+ @Mock
+ private HttpRequest request;
+ @Mock
+ private HttpResponse response;
+ @Mock
+ private EnhancedClassInstanceContext classInstanceContext;
+ @Mock
+ private InstanceMethodInvokeContext methodInvokeContext;
+ @Mock
+ private MethodInterceptResult methodInterceptResult;
+
+ @Before
+ public void setUp() throws Exception {
+
+ ServiceManager.INSTANCE.boot();
+
+ interceptor = new ResinV3Interceptor();
+ contextListener = new MockTracerContextListener();
+
+ TracerContext.ListenerManager.add(contextListener);
+
+ when(request.getRequestURI()).thenReturn("/test/testRequestURL");
+ when(request.getRequestURL()).thenReturn(new StringBuffer("http://localhost:8080/test/testRequestURL"));
+ when(response.getStatusCode()).thenReturn(200);
+ when(methodInvokeContext.allArguments()).thenReturn(new Object[] {request, response});
+ }
+
+ @Test
+ public void testWithoutSerializedContextData() {
+ interceptor.beforeMethod(classInstanceContext, methodInvokeContext, methodInterceptResult);
+ interceptor.afterMethod(classInstanceContext, methodInvokeContext, null);
+
+ contextListener.assertSize(1);
+ contextListener.assertTraceSegment(0, new SegmentAssert() {
+ @Override
+ public void call(TraceSegment traceSegment) {
+ assertThat(traceSegment.getSpans().size(), is(1));
+ Span span = traceSegment.getSpans().get(0);
+ assertHttpSpan(span);
+ }
+ });
+ }
+
+ @Test
+ public void testWithSerializedContextData() {
+ when(request.getHeader(ResinV3Interceptor.HEADER_NAME_OF_CONTEXT_DATA)).thenReturn("302017.1487666919810.624424584.17332.1.1|1|REMOTE_APP|127.0.0.1|Trace.globalId.123|1");
+
+ interceptor.beforeMethod(classInstanceContext, methodInvokeContext, methodInterceptResult);
+ interceptor.afterMethod(classInstanceContext, methodInvokeContext, null);
+
+ contextListener.assertSize(1);
+ contextListener.assertTraceSegment(0, new SegmentAssert() {
+ @Override
+ public void call(TraceSegment traceSegment) {
+ assertThat(traceSegment.getSpans().size(), is(1));
+ Span span = traceSegment.getSpans().get(0);
+ assertHttpSpan(span);
+ assertTraceSegmentRef(traceSegment.getRefs().get(0));
+ }
+ });
+ }
+
+ @Test
+ public void testWithOccurException() {
+ interceptor.beforeMethod(classInstanceContext, methodInvokeContext, methodInterceptResult);
+ interceptor.handleMethodException(new RuntimeException(), classInstanceContext, methodInvokeContext);
+ interceptor.afterMethod(classInstanceContext, methodInvokeContext, null);
+
+ contextListener.assertSize(1);
+ contextListener.assertTraceSegment(0, new SegmentAssert() {
+ @Override
+ public void call(TraceSegment traceSegment) {
+ assertThat(traceSegment.getSpans().size(), is(1));
+ Span span = traceSegment.getSpans().get(0);
+ assertHttpSpan(span);
+ assertThat(span.getLogs().size(), is(1));
+ assertSpanLog(span.getLogs().get(0));
+ }
+ });
+ }
+
+ private void assertSpanLog(LogData logData) {
+ assertThat(logData.getFields().size(), is(4));
+ assertThat(logData.getFields().get("event"), CoreMatchers.