diff --git a/apm-network/src/main/java/org/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-network/src/main/java/org/skywalking/apm/network/trace/component/ComponentsDefine.java
index 80affad29..5c9b7c0ef 100644
--- a/apm-network/src/main/java/org/skywalking/apm/network/trace/component/ComponentsDefine.java
+++ b/apm-network/src/main/java/org/skywalking/apm/network/trace/component/ComponentsDefine.java
@@ -35,7 +35,11 @@ public class ComponentsDefine {
public static final OfficialComponent SPRING_MVC_ANNOTATION = new OfficialComponent(14, "SpringMVC");
- public static final OfficialComponent STRUTS2 = new OfficialComponent(14, "Struts2");
+ public static final OfficialComponent STRUTS2 = new OfficialComponent(15, "Struts2");
+
+ public static final OfficialComponent NUTZ_MVC_ANNOTATION = new OfficialComponent(16, "NutzMVC");
+
+ public static final OfficialComponent NUTZ_HTTP = new OfficialComponent(17, "NutzHttp");
private static ComponentsDefine instance = new ComponentsDefine();
@@ -46,7 +50,7 @@ public class ComponentsDefine {
}
public ComponentsDefine() {
- components = new String[16];
+ components = new String[18];
addComponent(TOMCAT);
addComponent(HTTPCLIENT);
addComponent(DUBBO);
@@ -62,6 +66,8 @@ public class ComponentsDefine {
addComponent(SPRING_REST_TEMPLATE);
addComponent(SPRING_MVC_ANNOTATION);
addComponent(STRUTS2);
+ addComponent(NUTZ_MVC_ANNOTATION);
+ addComponent(NUTZ_HTTP);
}
private void addComponent(OfficialComponent component) {
diff --git a/apm-sniffer/apm-agent/pom.xml b/apm-sniffer/apm-agent/pom.xml
index 53ea8e30c..0aa99ea71 100644
--- a/apm-sniffer/apm-agent/pom.xml
+++ b/apm-sniffer/apm-agent/pom.xml
@@ -105,6 +105,16 @@
apm-struts2-2.x-plugin
${project.version}
+
+ org.skywalking
+ apm-nutz-mvc-annotation-1.x-plugin
+ ${project.version}
+
+
+ org.skywalking
+ apm-nutz-http-1.x-plugin
+ ${project.version}
+
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/pom.xml
new file mode 100644
index 000000000..defd73beb
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/pom.xml
@@ -0,0 +1,24 @@
+
+
+ nutz-plugins
+ org.skywalking
+ 3.2.1-2017
+
+ 4.0.0
+
+ apm-nutz-http-1.x-plugin
+ jar
+
+ http-1.x-plugin
+ http://maven.apache.org
+
+
+
+ org.nutz
+ nutz
+ 1.r.62
+ provided
+
+
+
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/SenderConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/SenderConstructorInterceptor.java
new file mode 100644
index 000000000..640b6e822
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/SenderConstructorInterceptor.java
@@ -0,0 +1,12 @@
+package org.skywalking.apm.plugin.nutz.http.sync;
+
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+
+public class SenderConstructorInterceptor implements InstanceConstructorInterceptor {
+
+ @Override
+ public void onConstruct(final EnhancedInstance objInst, final Object[] allArguments) {
+ objInst.setSkyWalkingDynamicField(allArguments[0]);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/SenderSendInterceptor.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/SenderSendInterceptor.java
new file mode 100644
index 000000000..b7326ad31
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/SenderSendInterceptor.java
@@ -0,0 +1,63 @@
+package org.skywalking.apm.plugin.nutz.http.sync;
+
+import java.lang.reflect.Method;
+import java.net.URI;
+
+import org.nutz.http.Request;
+import org.nutz.http.Request.METHOD;
+import org.nutz.http.Response;
+import org.skywalking.apm.agent.core.context.CarrierItem;
+import org.skywalking.apm.agent.core.context.ContextCarrier;
+import org.skywalking.apm.agent.core.context.ContextManager;
+import org.skywalking.apm.agent.core.context.tag.Tags;
+import org.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.skywalking.apm.network.trace.component.ComponentsDefine;
+
+public class SenderSendInterceptor implements InstanceMethodsAroundInterceptor {
+
+ @Override
+ public void beforeMethod(final EnhancedInstance objInst, final Method method, final Object[] allArguments, final Class>[] argumentsTypes,
+ final MethodInterceptResult result) throws Throwable {
+ Request req = (Request) objInst.getSkyWalkingDynamicField();
+ final URI requestURL = req.getUrl().toURI();
+ final METHOD httpMethod = req.getMethod();
+ final ContextCarrier contextCarrier = new ContextCarrier();
+ String remotePeer = requestURL.getHost() + ":" + requestURL.getPort();
+ AbstractSpan span = ContextManager.createExitSpan(requestURL.getPath(), contextCarrier, remotePeer);
+
+ span.setComponent(ComponentsDefine.NUTZ_HTTP);
+ Tags.URL.set(span, requestURL.getScheme() + "://" + requestURL.getHost() + ":" + requestURL.getPort() + requestURL.getPath());
+ Tags.HTTP.METHOD.set(span, httpMethod.toString());
+ SpanLayer.asHttp(span);
+
+ CarrierItem next = contextCarrier.items();
+ while (next.hasNext()) {
+ next = next.next();
+ req.getHeader().set(next.getHeadKey(), next.getHeadValue());
+ }
+ }
+
+ @Override
+ public Object afterMethod(final EnhancedInstance objInst, final Method method, final Object[] allArguments, final Class>[] argumentsTypes,
+ Object ret) throws Throwable {
+ Response response = (Response)ret;
+ int statusCode = response.getStatus();
+ AbstractSpan span = ContextManager.activeSpan();
+ if (statusCode >= 400) {
+ span.errorOccurred();
+ Tags.STATUS_CODE.set(span, Integer.toString(statusCode));
+ }
+ ContextManager.stopSpan();
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(final EnhancedInstance objInst, final Method method, final Object[] allArguments,
+ final Class>[] argumentsTypes, final Throwable t) {
+ ContextManager.activeSpan().errorOccurred().log(t);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/AbstractNutzHttpInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/AbstractNutzHttpInstrumentation.java
new file mode 100644
index 000000000..e4b372866
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/AbstractNutzHttpInstrumentation.java
@@ -0,0 +1,60 @@
+package org.skywalking.apm.plugin.nutz.http.sync.define;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+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.agent.core.plugin.match.ClassMatch;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+
+public abstract class AbstractNutzHttpInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String DO_SEND_METHOD_NAME = "send";
+ private static final String DO_SEND_INTERCEPTOR = "org.skywalking.apm.plugin.nutz.http.sync.SenderSendInterceptor";
+ private static final String DO_CONSTRUCTOR_INTERCEPTOR = "org.skywalking.apm.plugin.nutz.http.sync.SenderConstructorInterceptor";
+
+ @Override
+ protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[]{
+ new ConstructorInterceptPoint() {
+ @Override
+ public ElementMatcher getConstructorMatcher() {
+ return ElementMatchers.takesArguments(1);
+ }
+
+ @Override
+ public String getConstructorInterceptor() {
+ return DO_CONSTRUCTOR_INTERCEPTOR;
+ }
+ }
+ };
+ }
+
+ @Override
+ protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[]{
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named(DO_SEND_METHOD_NAME);
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return DO_SEND_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+
+ protected abstract ClassMatch enhanceClass();
+}
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/NutzHttpFilePostSenderInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/NutzHttpFilePostSenderInstrumentation.java
new file mode 100644
index 000000000..9082a9e57
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/NutzHttpFilePostSenderInstrumentation.java
@@ -0,0 +1,11 @@
+package org.skywalking.apm.plugin.nutz.http.sync.define;
+
+import org.skywalking.apm.agent.core.plugin.match.ClassMatch;
+import org.skywalking.apm.agent.core.plugin.match.NameMatch;
+
+public class NutzHttpFilePostSenderInstrumentation extends AbstractNutzHttpInstrumentation {
+
+ protected ClassMatch enhanceClass() {
+ return NameMatch.byName("org.nutz.http.sender.FilePostSender");
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/NutzHttpGetSenderInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/NutzHttpGetSenderInstrumentation.java
new file mode 100644
index 000000000..28bf2a1a6
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/NutzHttpGetSenderInstrumentation.java
@@ -0,0 +1,12 @@
+package org.skywalking.apm.plugin.nutz.http.sync.define;
+
+import org.skywalking.apm.agent.core.plugin.match.ClassMatch;
+import org.skywalking.apm.agent.core.plugin.match.NameMatch;
+
+public class NutzHttpGetSenderInstrumentation extends AbstractNutzHttpInstrumentation {
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return NameMatch.byName("org.nutz.http.sender.GetSender");
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/NutzHttpPostSenderInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/NutzHttpPostSenderInstrumentation.java
new file mode 100644
index 000000000..f1ed889f0
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/http/sync/define/NutzHttpPostSenderInstrumentation.java
@@ -0,0 +1,13 @@
+package org.skywalking.apm.plugin.nutz.http.sync.define;
+
+import org.skywalking.apm.agent.core.plugin.match.ClassMatch;
+import org.skywalking.apm.agent.core.plugin.match.NameMatch;
+
+
+public class NutzHttpPostSenderInstrumentation extends AbstractNutzHttpInstrumentation {
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return NameMatch.byName("org.nutz.http.sender.PostSender");
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 000000000..203a31f4d
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1,3 @@
+nutz-http-1.x=org.skywalking.apm.plugin.nutz.http.sync.define.NutzHttpGetSenderInstrumentation
+nutz-http-1.x=org.skywalking.apm.plugin.nutz.http.sync.define.NutzHttpPostSenderInstrumentation
+nutz-http-1.x=org.skywalking.apm.plugin.nutz.http.sync.define.NutzHttpFilePostSenderInstrumentation
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/test/java/org/skywalking/apm/plugin/nutz/http/sync/SenderInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/test/java/org/skywalking/apm/plugin/nutz/http/sync/SenderInterceptorTest.java
new file mode 100644
index 000000000..61bc40f1d
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/src/test/java/org/skywalking/apm/plugin/nutz/http/sync/SenderInterceptorTest.java
@@ -0,0 +1,114 @@
+package org.skywalking.apm.plugin.nutz.http.sync;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.nutz.http.Request;
+import org.nutz.http.Request.METHOD;
+import org.nutz.http.Response;
+import org.nutz.http.Sender;
+import org.nutz.http.sender.FilePostSender;
+import org.nutz.http.sender.GetSender;
+import org.nutz.http.sender.PostSender;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+import org.skywalking.apm.agent.core.boot.ServiceManager;
+import org.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
+import org.skywalking.apm.agent.core.context.trace.TraceSegment;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.skywalking.apm.agent.test.helper.SegmentHelper;
+import org.skywalking.apm.agent.test.tools.AgentServiceRule;
+import org.skywalking.apm.agent.test.tools.SegmentStorage;
+import org.skywalking.apm.agent.test.tools.SegmentStoragePoint;
+import org.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+
+@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
+@PowerMockRunnerDelegate(TracingSegmentRunner.class)
+public class SenderInterceptorTest {
+
+ @SegmentStoragePoint
+ public SegmentStorage segmentStorage;
+
+ @Rule
+ public AgentServiceRule serviceRule = new AgentServiceRule();
+
+ @Mock
+ private EnhancedInstance enhancedInstance;
+
+ @Mock
+ Response resp;
+
+ SenderConstructorInterceptor constructorInterceptPoint;
+
+ SenderSendInterceptor senderSendInterceptor;
+
+ Method sendMethod;
+ Object[] allArguments;
+ Class>[] argumentsTypes;
+
+ @Before
+ public void setUp() throws Exception {
+ ServiceManager.INSTANCE.boot();
+ constructorInterceptPoint = new SenderConstructorInterceptor();
+ senderSendInterceptor = new SenderSendInterceptor();
+ }
+
+ public void setupSender(Class extends Sender> klass) throws NoSuchMethodException, SecurityException {
+ sendMethod = klass.getMethod("send");
+ allArguments = new Object[0];
+ argumentsTypes = new Class>[0];
+ }
+
+ @Test
+ public void test_constructor() {
+ Request request = Request.create("https://nutz.cn/yvr/list", METHOD.GET);
+ constructorInterceptPoint.onConstruct(enhancedInstance, new Object[]{request});
+ verify(enhancedInstance, times(1)).setSkyWalkingDynamicField(request);
+ }
+
+ @Test
+ public void test_getsender_send() throws NoSuchMethodException, SecurityException, Throwable {
+ setupSender(GetSender.class);
+ _sender_sender_test();
+ }
+
+ @Test
+ public void test_postsender_send() throws NoSuchMethodException, SecurityException, Throwable {
+ setupSender(PostSender.class);
+ _sender_sender_test();
+ }
+
+ @Test
+ public void test_filepostsender_send() throws NoSuchMethodException, SecurityException, Throwable {
+ setupSender(FilePostSender.class);
+ _sender_sender_test();
+ }
+
+ protected void _sender_sender_test() throws Throwable {
+ Request request = Request.create("https://nutz.cn/yvr/list", METHOD.GET);
+ constructorInterceptPoint.onConstruct(enhancedInstance, new Object[]{request});
+ verify(enhancedInstance, times(1)).setSkyWalkingDynamicField(request);
+
+ when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn(request);
+ when(resp.getStatus()).thenReturn(200);
+
+ senderSendInterceptor.beforeMethod(enhancedInstance, sendMethod, allArguments, argumentsTypes, null);
+ senderSendInterceptor.afterMethod(enhancedInstance, sendMethod, allArguments, argumentsTypes, resp);
+
+ TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
+ List spans = SegmentHelper.getSpans(traceSegment);
+ assertThat(spans.size(), is(1));
+ assertThat(spans.get(0).getOperationName(), is("/yvr/list"));
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/pom.xml
new file mode 100644
index 000000000..b71d4f8ac
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/pom.xml
@@ -0,0 +1,30 @@
+
+
+ nutz-plugins
+ org.skywalking
+ 3.2.1-2017
+
+ 4.0.0
+
+ apm-nutz-mvc-annotation-1.x-plugin
+ jar
+
+ mvc-annotation-1.x-plugin
+ http://maven.apache.org
+
+
+
+ org.nutz
+ nutz
+ 1.r.62
+ provided
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.1.0
+ provided
+
+
+
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/ActionConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/ActionConstructorInterceptor.java
new file mode 100644
index 000000000..930e041de
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/ActionConstructorInterceptor.java
@@ -0,0 +1,23 @@
+package org.skywalking.apm.plugin.nutz.mvc;
+
+import org.nutz.mvc.annotation.At;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+
+/**
+ *
+ * @author wendal
+ */
+public class ActionConstructorInterceptor implements InstanceConstructorInterceptor {
+
+ @Override
+ public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
+ String basePath = "";
+ At basePathRequestMapping = objInst.getClass().getAnnotation(At.class);
+ if (basePathRequestMapping != null) {
+ basePath = basePathRequestMapping.value()[0];
+ }
+ PathMappingCache pathMappingCache = new PathMappingCache(basePath);
+ objInst.setSkyWalkingDynamicField(pathMappingCache);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/ActionMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/ActionMethodInterceptor.java
new file mode 100644
index 000000000..ab96e057a
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/ActionMethodInterceptor.java
@@ -0,0 +1,76 @@
+package org.skywalking.apm.plugin.nutz.mvc;
+
+import java.lang.reflect.Method;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.nutz.mvc.Mvcs;
+import org.nutz.mvc.annotation.At;
+import org.skywalking.apm.agent.core.context.CarrierItem;
+import org.skywalking.apm.agent.core.context.ContextCarrier;
+import org.skywalking.apm.agent.core.context.ContextManager;
+import org.skywalking.apm.agent.core.context.tag.Tags;
+import org.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.skywalking.apm.network.trace.component.ComponentsDefine;
+
+/**
+ * The ActionMethodInterceptor only use the first mapping value.
+ *
+ * @See {@link ActionConstructorInterceptor} to explain why we are doing this.
+ * @author wendal
+ */
+public class ActionMethodInterceptor implements InstanceMethodsAroundInterceptor {
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ MethodInterceptResult result) throws Throwable {
+ PathMappingCache pathMappingCache = (PathMappingCache)objInst.getSkyWalkingDynamicField();
+ String requestURL = pathMappingCache.findPathMapping(method);
+ if (requestURL == null) {
+ At methodRequestMapping = method.getAnnotation(At.class);
+ if (methodRequestMapping.value().length > 0) {
+ requestURL = methodRequestMapping.value()[0];
+ } else {
+ requestURL = "";
+ }
+ pathMappingCache.addPathMapping(method, requestURL);
+ requestURL = pathMappingCache.findPathMapping(method);
+ }
+
+ HttpServletRequest request = Mvcs.getReq();
+ ContextCarrier contextCarrier = new ContextCarrier();
+ CarrierItem next = contextCarrier.items();
+ while (next.hasNext()) {
+ next = next.next();
+ next.setHeadValue(request.getHeader(next.getHeadKey()));
+ }
+ AbstractSpan span = ContextManager.createEntrySpan(requestURL, contextCarrier);
+ Tags.URL.set(span, request.getRequestURL().toString());
+ Tags.HTTP.METHOD.set(span, request.getMethod());
+ span.setComponent(ComponentsDefine.NUTZ_MVC_ANNOTATION);
+ SpanLayer.asHttp(span);
+ }
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ Object ret) throws Throwable {
+ HttpServletResponse response = Mvcs.getResp();
+
+ 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);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/PathMappingCache.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/PathMappingCache.java
new file mode 100644
index 000000000..7df1b31ba
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/PathMappingCache.java
@@ -0,0 +1,29 @@
+package org.skywalking.apm.plugin.nutz.mvc;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * The PathMappingCache represents a mapping cache.
+ * key: {@link Method}
+ * value: the url pattern
+ *
+ * @author wendal
+ */
+public class PathMappingCache {
+ private String classPath = "";
+
+ private ConcurrentHashMap methodPathMapping = new ConcurrentHashMap();
+
+ public PathMappingCache(String classPath) {
+ this.classPath = classPath;
+ }
+
+ public String findPathMapping(Method method) {
+ return methodPathMapping.get(method);
+ }
+
+ public void addPathMapping(Method method, String methodPath) {
+ methodPathMapping.put(method, classPath + methodPath);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/define/ActionInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/define/ActionInstrumentation.java
new file mode 100644
index 000000000..406789852
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/java/org/skywalking/apm/plugin/nutz/mvc/define/ActionInstrumentation.java
@@ -0,0 +1,67 @@
+package org.skywalking.apm.plugin.nutz.mvc.define;
+
+import static net.bytebuddy.matcher.ElementMatchers.any;
+import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static org.skywalking.apm.agent.core.plugin.match.ClassAnnotationMatch.byClassAnnotationMatch;
+
+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.agent.core.plugin.match.ClassMatch;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+public class ActionInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ public static final String ENHANCE_ANNOTATION = "org.nutz.mvc.annotation.At";
+
+ @Override
+ protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[] {
+ new ConstructorInterceptPoint() {
+ @Override
+ public ElementMatcher getConstructorMatcher() {
+ return any();
+ }
+
+ @Override
+ public String getConstructorInterceptor() {
+ return "org.skywalking.apm.plugin.nutz.mvc.ActionConstructorInterceptor";
+ }
+ }
+ };
+ }
+
+ @Override
+ protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[] {
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return isAnnotatedWith(named("org.nutz.mvc.annotation.At"));
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return "org.skywalking.apm.plugin.nutz.mvc.ActionMethodInterceptor";
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return byClassAnnotationMatch(getEnhanceAnnotations());
+ }
+
+ protected String[] getEnhanceAnnotations() {
+ return new String[] {ENHANCE_ANNOTATION};
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 000000000..c6566dcf3
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1 @@
+nutz-mvc-annotation-1.x=org.skywalking.apm.plugin.nutz.mvc.define.ActionInstrumentation
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/nutz-plugins/pom.xml
new file mode 100644
index 000000000..06b11ae04
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+
+ org.skywalking
+ apm-sdk-plugin
+ 3.2.1-2017
+
+
+ nutz-plugins
+
+
+ http-1.x-plugin
+ mvc-annotation-1.x-plugin
+
+ pom
+
+ apm-sdk-plugin
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml
index 54bfee03c..33ce19e05 100644
--- a/apm-sniffer/apm-sdk-plugin/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/pom.xml
@@ -24,6 +24,7 @@
resin-4.x-plugin
spring-plugins
struts2-2.x-plugin
+ nutz-plugins
pom