diff --git a/.github/workflows/plugins-test.0.yaml b/.github/workflows/plugins-test.0.yaml index 3c2b3553fd..7a083fffba 100644 --- a/.github/workflows/plugins-test.0.yaml +++ b/.github/workflows/plugins-test.0.yaml @@ -67,6 +67,7 @@ jobs: - correlation-autotag-scenario - dubbo-2.5.x-scenario - dubbo-2.7.x-scenario + - dubbo-3.x-scenario - ehcache-2.x-scenario - elasticsearch-5.x-scenario - elasticsearch-6.x-scenario diff --git a/CHANGES.md b/CHANGES.md index 185505d467..7ce0a1215b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ Release Notes. * Avoid `ProfileTaskChannelService.addProfilingSnapshot` throw IllegalStateException(Queue full) * Increase `ProfileTaskChannelService.snapshotQueue` default size from 50 to 4500 * Support 2.8 and 2.9 of pulsar client. +* Add dubbo 3.x plugin. #### Documentation diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo/patch/WrapperInstrumentation.java b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo/patch/WrapperInstrumentation.java index c1fce7153d..db281670f6 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo/patch/WrapperInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo/patch/WrapperInstrumentation.java @@ -19,11 +19,16 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassStaticMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import java.util.Collections; +import java.util.List; + import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; /** @@ -35,6 +40,11 @@ * to ensure the correct compilation of the code. */ public class WrapperInstrumentation extends ClassStaticMethodsEnhancePluginDefine { + + private static final String CONTEXT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContext"; + + private static final String GET_SERVER_CONTEXT_METHOD_NAME = "getServerContext"; + @Override public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() { return new StaticMethodsInterceptPoint[] { @@ -61,4 +71,14 @@ public boolean isOverrideArgs() { protected ClassMatch enhanceClass() { return byName("org.apache.dubbo.common.bytecode.Wrapper"); } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + CONTEXT_TYPE_NAME, + named(GET_SERVER_CONTEXT_METHOD_NAME).and( + returns(named(CONTEXT_TYPE_NAME))) + )); + } + } diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/src/main/resources/skywalking-plugin.def index d26e43aa27..2d21aab818 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/src/main/resources/skywalking-plugin.def @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -dubbo=org.apache.skywalking.apm.plugin.asf.dubbo.patch.WrapperInstrumentation +dubbo-2.7.x=org.apache.skywalking.apm.plugin.asf.dubbo.patch.WrapperInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo/DubboInstrumentation.java b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo/DubboInstrumentation.java index 1de40037d9..e4cb06f670 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo/DubboInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo/DubboInstrumentation.java @@ -20,13 +20,18 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; 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 java.util.Collections; +import java.util.List; + import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; public class DubboInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { @@ -34,6 +39,10 @@ public class DubboInstrumentation extends ClassInstanceMethodsEnhancePluginDefin private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.asf.dubbo.DubboInterceptor"; + private static final String CONTEXT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContext"; + + private static final String GET_SERVER_CONTEXT_METHOD_NAME = "getServerContext"; + @Override protected ClassMatch enhanceClass() { return NameMatch.byName(ENHANCE_CLASS); @@ -65,4 +74,14 @@ public boolean isOverrideArgs() { } }; } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + CONTEXT_TYPE_NAME, + named(GET_SERVER_CONTEXT_METHOD_NAME).and( + returns(named(CONTEXT_TYPE_NAME))) + )); + } + } diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/src/main/resources/skywalking-plugin.def index cd31eec95c..f6b64e0ba3 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/src/main/resources/skywalking-plugin.def @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -dubbo=org.apache.skywalking.apm.plugin.asf.dubbo.DubboInstrumentation +dubbo-2.7.x=org.apache.skywalking.apm.plugin.asf.dubbo.DubboInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/pom.xml b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/pom.xml new file mode 100644 index 0000000000..4caf21d142 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/pom.xml @@ -0,0 +1,44 @@ + + + + + + apm-sdk-plugin + org.apache.skywalking + 8.9.0-SNAPSHOT + + 4.0.0 + + dubbo-3.x-conflict-patch + + apm-dubbo-3.x-conflict-path + + UTF-8 + 3.0.0 + + + + + org.apache.dubbo + dubbo + ${dubbo.version} + provided + + + + diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/patch/MakeWrapperInterceptor.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/patch/MakeWrapperInterceptor.java new file mode 100644 index 0000000000..e5b47d6bb2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/patch/MakeWrapperInterceptor.java @@ -0,0 +1,329 @@ +/* + * 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.asf.dubbo3.patch; + +import org.apache.dubbo.common.bytecode.ClassGenerator; +import org.apache.dubbo.common.bytecode.NoSuchPropertyException; +import org.apache.dubbo.common.bytecode.Wrapper; +import org.apache.dubbo.common.utils.ClassHelper; +import org.apache.dubbo.common.utils.ReflectUtils; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.util.regex.Matcher; +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.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor; + +public class MakeWrapperInterceptor implements StaticMethodsAroundInterceptor { + + private static final AtomicLong WRAPPER_CLASS_COUNTER = new AtomicLong(0); + + @Override + public void beforeMethod(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, + MethodInterceptResult result) { + Class wrapperClass = (Class) allArguments[0]; + if (EnhancedInstance.class.isAssignableFrom(wrapperClass)) { + result.defineReturnValue(makeWrapper(wrapperClass)); + } + } + + @Override + public Object afterMethod(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, + Object ret) { + return ret; + } + + @Override + public void handleMethodException(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, + Throwable t) { + } + + private static Wrapper makeWrapper(Class c) { + if (c.isPrimitive()) + throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c); + + String name = c.getName(); + ClassLoader cl = ClassHelper.getClassLoader(c); + + StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ "); + StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ "); + StringBuilder c3 = new StringBuilder( + "public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class + .getName() + "{ "); + + c1.append(name) + .append(" w; try{ w = ((") + .append(name) + .append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }"); + c2.append(name) + .append(" w; try{ w = ((") + .append(name) + .append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }"); + c3.append(name) + .append(" w; try{ w = ((") + .append(name) + .append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }"); + + Map> pts = new HashMap>(); // + Map ms = new LinkedHashMap(); // + List mns = new ArrayList(); // method names. + List dmns = new ArrayList(); // declaring method names. + + // get all public field. + for (Field f : c.getFields()) { + String fn = f.getName(); + Class ft = f.getType(); + if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers())) + continue; + + c1.append(" if( $2.equals(\"") + .append(fn) + .append("\") ){ w.") + .append(fn) + .append("=") + .append(arg(ft, "$3")) + .append("; return; }"); + c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }"); + pts.put(fn, ft); + } + + Method[] methods = c.getMethods(); + // get all public method. + boolean hasMethod = hasMethods(methods); + if (hasMethod) { + c3.append(" try{"); + } + for (Method m : methods) { + // ignore Object's method + // ignore EnhanceInstance's method + if (m.getDeclaringClass() == Object.class || "getSkyWalkingDynamicField".equals( + m.getName()) || "setSkyWalkingDynamicField" + .equals(m.getName())) { + continue; + } + + String mn = m.getName(); + c3.append(" if( \"").append(mn).append("\".equals( $2 ) "); + int len = m.getParameterTypes().length; + c3.append(" && ").append(" $3.length == ").append(len); + + boolean override = false; + for (Method m2 : methods) { + if (!m.equals(m2) && m.getName().equals(m2.getName())) { + override = true; + break; + } + } + if (override) { + if (len > 0) { + for (int l = 0; l < len; l++) { + c3.append(" && ") + .append(" $3[") + .append(l) + .append("].getName().equals(\"") + .append(m.getParameterTypes()[l].getName()) + .append("\")"); + } + } + } + + c3.append(" ) { "); + + if (m.getReturnType() == Void.TYPE) + c3.append(" w.") + .append(mn) + .append('(') + .append(args(m.getParameterTypes(), "$4")) + .append(");") + .append(" return null;"); + else + c3.append(" return ($w)w.") + .append(mn) + .append('(') + .append(args(m.getParameterTypes(), "$4")) + .append(");"); + + c3.append(" }"); + + mns.add(mn); + if (m.getDeclaringClass() == c) + dmns.add(mn); + ms.put(ReflectUtils.getDesc(m), m); + } + if (hasMethod) { + c3.append(" } catch(Throwable e) { "); + c3.append(" throw new java.lang.reflect.InvocationTargetException(e); "); + c3.append(" }"); + } + + c3.append( + " throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c + .getName() + ".\"); }"); + + // deal with get/set method. + Matcher matcher; + for (Map.Entry entry : ms.entrySet()) { + String md = entry.getKey(); + Method method = (Method) entry.getValue(); + if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) { + String pn = propertyName(matcher.group(1)); + c2.append(" if( $2.equals(\"") + .append(pn) + .append("\") ){ return ($w)w.") + .append(method.getName()) + .append("(); }"); + pts.put(pn, method.getReturnType()); + } else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) { + String pn = propertyName(matcher.group(1)); + c2.append(" if( $2.equals(\"") + .append(pn) + .append("\") ){ return ($w)w.") + .append(method.getName()) + .append("(); }"); + pts.put(pn, method.getReturnType()); + } else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) { + Class pt = method.getParameterTypes()[0]; + String pn = propertyName(matcher.group(1)); + c1.append(" if( $2.equals(\"") + .append(pn) + .append("\") ){ w.") + .append(method.getName()) + .append("(") + .append(arg(pt, "$3")) + .append("); return; }"); + pts.put(pn, pt); + } + } + c1.append( + " throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c + .getName() + ".\"); }"); + c2.append( + " throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c + .getName() + ".\"); }"); + + // make class + long id = WRAPPER_CLASS_COUNTER.getAndIncrement(); + ClassGenerator cc = ClassGenerator.newInstance(cl); + cc.setClassName((Modifier.isPublic( + c.getModifiers()) ? Wrapper.class.getName() + "_swEnhance" : c.getName() + "$sw$_swEnhance") + id); + cc.setSuperClass(Wrapper.class); + + cc.addDefaultConstructor(); + cc.addField("public static String[] pns;"); // property name array. + cc.addField("public static " + Map.class.getName() + " pts;"); // property type map. + cc.addField("public static String[] mns;"); // all method name array. + cc.addField("public static String[] dmns;"); // declared method name array. + for (int i = 0, len = ms.size(); i < len; i++) + cc.addField("public static Class[] mts" + i + ";"); + + cc.addMethod("public String[] getPropertyNames(){ return pns; }"); + cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }"); + cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }"); + cc.addMethod("public String[] getMethodNames(){ return mns; }"); + cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }"); + cc.addMethod(c1.toString()); + cc.addMethod(c2.toString()); + cc.addMethod(c3.toString()); + + try { + Class wc = cc.toClass(); + // setup static field. + wc.getField("pts").set(null, pts); + wc.getField("pns").set(null, pts.keySet().toArray(new String[0])); + wc.getField("mns").set(null, mns.toArray(new String[0])); + wc.getField("dmns").set(null, dmns.toArray(new String[0])); + int ix = 0; + for (Method m : ms.values()) + wc.getField("mts" + ix++).set(null, m.getParameterTypes()); + return (Wrapper) wc.getDeclaredConstructor().newInstance(); + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + cc.release(); + ms.clear(); + mns.clear(); + dmns.clear(); + } + } + + private static String args(Class[] cs, String name) { + int len = cs.length; + if (len == 0) + return ""; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len; i++) { + if (i > 0) + sb.append(','); + sb.append(arg(cs[i], name + "[" + i + "]")); + } + return sb.toString(); + } + + private static String propertyName(String pn) { + return pn.length() == 1 || Character.isLowerCase(pn.charAt(1)) ? Character.toLowerCase( + pn.charAt(0)) + pn.substring(1) : pn; + } + + private static boolean hasMethods(Method[] methods) { + if (methods == null || methods.length == 0) { + return false; + } + for (Method m : methods) { + if (m.getDeclaringClass() != Object.class && !"getSkyWalkingDynamicField".equals( + m.getName()) && !"setSkyWalkingDynamicField" + .equals(m.getName())) { + return true; + } + } + return false; + } + + private static String arg(Class cl, String name) { + if (cl.isPrimitive()) { + if (cl == Boolean.TYPE) + return "((Boolean)" + name + ").booleanValue()"; + if (cl == Byte.TYPE) + return "((Byte)" + name + ").byteValue()"; + if (cl == Character.TYPE) + return "((Character)" + name + ").charValue()"; + if (cl == Double.TYPE) + return "((Number)" + name + ").doubleValue()"; + if (cl == Float.TYPE) + return "((Number)" + name + ").floatValue()"; + if (cl == Integer.TYPE) + return "((Number)" + name + ").intValue()"; + if (cl == Long.TYPE) + return "((Number)" + name + ").longValue()"; + if (cl == Short.TYPE) + return "((Number)" + name + ").shortValue()"; + throw new RuntimeException("Unknown primitive type: " + cl.getName()); + } + return "(" + ReflectUtils.getName(cl) + ")" + name; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/patch/WrapperInstrumentation.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/patch/WrapperInstrumentation.java new file mode 100644 index 0000000000..882d7a1ae1 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/patch/WrapperInstrumentation.java @@ -0,0 +1,87 @@ +/* + * 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.asf.dubbo3.patch; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassStaticMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * The dubbo conflict plugin resolver the problem about the wrapper class generated by Dubbo core cannot be compiled + * successfully. As we known, The wrapper class traverses all the methods. In usual, it works unless this class has been + * enhanced by Skywalking. The javasist cannot found the `EnhanceInstance` method when generate. + *

+ * The plugin excludes {@link org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance} methods + * to ensure the correct compilation of the code. + */ +public class WrapperInstrumentation extends ClassStaticMethodsEnhancePluginDefine { + + private static final String CONTEXT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContext"; + + private static final String GET_SERVER_CONTEXT_METHOD_NAME = "getServerContext"; + + private static final String CONTEXT_ATTACHMENT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContextAttachment"; + + @Override + public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() { + return new StaticMethodsInterceptPoint[] { + new StaticMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("makeWrapper"); + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.asf.dubbo3.patch.MakeWrapperInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return byName("org.apache.dubbo.common.bytecode.Wrapper"); + } + + @Override + protected List witnessMethods() { + return Collections.singletonList( + new WitnessMethod( + CONTEXT_TYPE_NAME, + named(GET_SERVER_CONTEXT_METHOD_NAME).and( + returns(named(CONTEXT_ATTACHMENT_TYPE_NAME))) + )); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..f33f5b4eb6 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/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. + +dubbo-3.x=org.apache.skywalking.apm.plugin.asf.dubbo3.patch.WrapperInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/pom.xml new file mode 100644 index 0000000000..825615323d --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/pom.xml @@ -0,0 +1,46 @@ + + + + + apm-sdk-plugin + org.apache.skywalking + 8.9.0-SNAPSHOT + + 4.0.0 + + apm-dubbo-3.x-plugin + jar + + dubbo-3.x-plugin + http://maven.apache.org + + + UTF-8 + 3.0.0 + + + + + org.apache.dubbo + dubbo + ${dubbo.version} + provided + + + diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboConsumerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboConsumerInstrumentation.java new file mode 100644 index 0000000000..27d206ccad --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboConsumerInstrumentation.java @@ -0,0 +1,27 @@ +/* + * 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.asf.dubbo3; + +public class DubboConsumerInstrumentation extends DubboInstrumentationBase { + + public DubboConsumerInstrumentation() { + super(CONSUMER_ENHANCE_CLASS); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboInstrumentationBase.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboInstrumentationBase.java new file mode 100644 index 0000000000..c983b9c0a0 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboInstrumentationBase.java @@ -0,0 +1,100 @@ +/* + * 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.asf.dubbo3; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; +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 java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; + +public class DubboInstrumentationBase extends ClassInstanceMethodsEnhancePluginDefine { + + public static final String PROVIDER_ENHANCE_CLASS = "org.apache.dubbo.monitor.support.MonitorFilter"; + + public static final String CONSUMER_ENHANCE_CLASS = "org.apache.dubbo.monitor.support.MonitorClusterFilter"; + + public static final String INTERCEPT_POINT_METHOD = "invoke"; + + public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.asf.dubbo3.DubboInterceptor"; + + public static final String CONTEXT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContext"; + + public static final String GET_SERVER_CONTEXT_METHOD_NAME = "getServerContext"; + + public static final String CONTEXT_ATTACHMENT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContextAttachment"; + + private final String enhanceClassName; + + public DubboInstrumentationBase(String enhanceClassName) { + this.enhanceClassName = enhanceClassName; + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(enhanceClassName); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return null; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(INTERCEPT_POINT_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected List witnessMethods() { + return Collections.singletonList( + new WitnessMethod( + CONTEXT_TYPE_NAME, + named(GET_SERVER_CONTEXT_METHOD_NAME).and( + returns(named(CONTEXT_ATTACHMENT_TYPE_NAME))) + )); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboInterceptor.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboInterceptor.java new file mode 100644 index 0000000000..0a374e9c0c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboInterceptor.java @@ -0,0 +1,217 @@ +/* + * 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.asf.dubbo3; + +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcContextAttachment; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.RpcServiceContext; +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.apache.skywalking.apm.util.StringUtil; + +import java.lang.reflect.Method; + +/** + * {@link DubboInterceptor} define how to enhance class {@link org.apache.dubbo.monitor.support.MonitorFilter#invoke(Invoker, + * Invocation)}. the trace context transport to the provider side by {@link RpcContext#getServerAttachment}.but all the + * version of dubbo framework below 3.0.0 don't support {@link RpcContext#getClientAttachment}, we support another way + * to support it. + */ +public class DubboInterceptor implements InstanceMethodsAroundInterceptor { + + public static final String ARGUMENTS = "arguments"; + + /** + *

Consumer:

The serialized trace context data will + * inject to the {@link RpcContext#getClientAttachment} for transport to provider side. + *

+ *

Provider:

The serialized trace context data will extract from + * {@link RpcContext#getServerAttachment}. current trace segment will ref if the serialize context data is not + * null. + */ + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + Invoker invoker = (Invoker) allArguments[0]; + Invocation invocation = (Invocation) allArguments[1]; + + RpcServiceContext serviceContext = RpcContext.getServiceContext(); + boolean isConsumer; + URL url = serviceContext.getUrl(); + if (url == null) { + url = serviceContext.getConsumerUrl(); + isConsumer = url != null; + } else { + isConsumer = serviceContext.isConsumerSide(); + } + + RpcContextAttachment attachment = isConsumer ? RpcContext.getClientAttachment() : RpcContext.getServerAttachment(); + URL requestURL = invoker.getUrl(); + + AbstractSpan span; + + final String host = requestURL.getHost(); + final int port = requestURL.getPort(); + + boolean needCollectArguments; + int argumentsLengthThreshold; + if (isConsumer) { + final ContextCarrier contextCarrier = new ContextCarrier(); + span = ContextManager.createExitSpan( + generateOperationName(requestURL, invocation), contextCarrier, host + ":" + port); + //invocation.getAttachments().put("contextData", contextDataStr); + //@see https://github.com/alibaba/dubbo/blob/dubbo-2.5.3/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java#L154-L161 + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + attachment.setAttachment(next.getHeadKey(), next.getHeadValue()); + if (invocation.getAttachments().containsKey(next.getHeadKey())) { + invocation.getAttachments().remove(next.getHeadKey()); + } + } + needCollectArguments = DubboPluginConfig.Plugin.Dubbo.COLLECT_CONSUMER_ARGUMENTS; + argumentsLengthThreshold = DubboPluginConfig.Plugin.Dubbo.CONSUMER_ARGUMENTS_LENGTH_THRESHOLD; + } else { + ContextCarrier contextCarrier = new ContextCarrier(); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + next.setHeadValue(attachment.getAttachment(next.getHeadKey())); + } + + span = ContextManager.createEntrySpan(generateOperationName(requestURL, invocation), contextCarrier); + span.setPeer(attachment.getRemoteAddressString()); + needCollectArguments = DubboPluginConfig.Plugin.Dubbo.COLLECT_PROVIDER_ARGUMENTS; + argumentsLengthThreshold = DubboPluginConfig.Plugin.Dubbo.PROVIDER_ARGUMENTS_LENGTH_THRESHOLD; + } + + Tags.URL.set(span, generateRequestURL(requestURL, invocation)); + collectArguments(needCollectArguments, argumentsLengthThreshold, span, invocation); + span.setComponent(ComponentsDefine.DUBBO); + SpanLayer.asRPCFramework(span); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + Result result = (Result) ret; + try { + if (result != null && result.getException() != null) { + dealException(result.getException()); + } + } catch (RpcException e) { + dealException(e); + } + + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + dealException(t); + } + + /** + * Log the throwable, which occurs in Dubbo RPC service. + */ + private void dealException(Throwable throwable) { + AbstractSpan span = ContextManager.activeSpan(); + span.log(throwable); + } + + /** + * Format operation name. e.g. org.apache.skywalking.apm.plugin.test.Test.test(String) + * + * @return operation name. + */ + private String generateOperationName(URL requestURL, Invocation invocation) { + StringBuilder operationName = new StringBuilder(); + String groupStr = requestURL.getParameter(CommonConstants.GROUP_KEY); + groupStr = StringUtil.isEmpty(groupStr) ? "" : groupStr + "/"; + operationName.append(groupStr); + operationName.append(requestURL.getPath()); + operationName.append("." + invocation.getMethodName() + "("); + for (Class classes : invocation.getParameterTypes()) { + operationName.append(classes.getSimpleName() + ","); + } + + if (invocation.getParameterTypes().length > 0) { + operationName.delete(operationName.length() - 1, operationName.length()); + } + + operationName.append(")"); + + return operationName.toString(); + } + + /** + * Format request url. e.g. dubbo://127.0.0.1:20880/org.apache.skywalking.apm.plugin.test.Test.test(String). + * + * @return request url. + */ + private String generateRequestURL(URL url, Invocation invocation) { + StringBuilder requestURL = new StringBuilder(); + requestURL.append(url.getProtocol() + "://"); + requestURL.append(url.getHost()); + requestURL.append(":" + url.getPort() + "/"); + requestURL.append(generateOperationName(url, invocation)); + return requestURL.toString(); + } + + private void collectArguments(boolean needCollectArguments, + int argumentsLengthThreshold, + AbstractSpan span, + Invocation invocation) { + if (needCollectArguments && argumentsLengthThreshold > 0) { + Object[] parameters = invocation.getArguments(); + if (parameters != null && parameters.length > 0) { + StringBuilder stringBuilder = new StringBuilder(); + boolean first = true; + for (Object parameter : parameters) { + if (!first) { + stringBuilder.append(","); + } + stringBuilder.append(parameter); + if (stringBuilder.length() > argumentsLengthThreshold) { + stringBuilder.append("..."); + break; + } + first = false; + } + span.tag(ARGUMENTS, stringBuilder.toString()); + } + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboPluginConfig.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboPluginConfig.java new file mode 100644 index 0000000000..4f009e136b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboPluginConfig.java @@ -0,0 +1,39 @@ +/* + * 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.asf.dubbo3; + +import org.apache.skywalking.apm.agent.core.boot.PluginConfig; + +public class DubboPluginConfig { + + public static class Plugin { + + @PluginConfig(root = DubboPluginConfig.class) + public static class Dubbo { + + public static boolean COLLECT_CONSUMER_ARGUMENTS = false; + + public static int CONSUMER_ARGUMENTS_LENGTH_THRESHOLD = 256; + + public static boolean COLLECT_PROVIDER_ARGUMENTS = false; + + public static int PROVIDER_ARGUMENTS_LENGTH_THRESHOLD = 256; + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboProviderInstrumentation.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboProviderInstrumentation.java new file mode 100644 index 0000000000..183c9bbab0 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/asf/dubbo3/DubboProviderInstrumentation.java @@ -0,0 +1,27 @@ +/* + * 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.asf.dubbo3; + +public class DubboProviderInstrumentation extends DubboInstrumentationBase { + + public DubboProviderInstrumentation() { + super(PROVIDER_ENHANCE_CLASS); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..4b5a95e797 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,18 @@ +# 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. + +dubbo-3.x=org.apache.skywalking.apm.plugin.asf.dubbo3.DubboProviderInstrumentation +dubbo-3.x=org.apache.skywalking.apm.plugin.asf.dubbo3.DubboConsumerInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/dubbo3/ContextManagerExtendOverrideService.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/dubbo3/ContextManagerExtendOverrideService.java new file mode 100644 index 0000000000..f6cc1bea3b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/dubbo3/ContextManagerExtendOverrideService.java @@ -0,0 +1,26 @@ +/* + * 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.dubbo3; + +import org.apache.skywalking.apm.agent.core.boot.OverrideImplementor; +import org.apache.skywalking.apm.agent.core.context.ContextManagerExtendService; + +@OverrideImplementor(ContextManagerExtendService.class) +public class ContextManagerExtendOverrideService extends ContextManagerExtendService { +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/dubbo3/DubboInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/dubbo3/DubboInterceptorTest.java new file mode 100644 index 0000000000..b432314ff8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/dubbo3/DubboInterceptorTest.java @@ -0,0 +1,242 @@ +/* + * 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.dubbo3; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; + +import java.util.List; + +import org.apache.dubbo.rpc.RpcContextAttachment; +import org.apache.dubbo.rpc.RpcServiceContext; +import org.apache.skywalking.apm.agent.core.boot.ServiceManager; +import org.apache.skywalking.apm.agent.core.conf.Config; +import org.apache.skywalking.apm.agent.core.context.ContextManagerExtendService; +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.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegmentRef; +import org.apache.skywalking.apm.agent.core.context.util.TagValuePair; +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.SegmentRefHelper; +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.TracingSegmentRunner; +import org.apache.skywalking.apm.plugin.asf.dubbo3.DubboInterceptor; +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.PowerMockRunnerDelegate; +import org.springframework.util.Assert; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(PowerMockRunner.class) +@PowerMockRunnerDelegate(TracingSegmentRunner.class) +@PrepareForTest({RpcContext.class}) +public class DubboInterceptorTest { + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Rule + public AgentServiceRule agentServiceRule = new AgentServiceRule(); + + @Mock + private EnhancedInstance enhancedInstance; + + private DubboInterceptor dubboInterceptor; + + @Mock + private RpcServiceContext serviceContext; + @Mock + private RpcContextAttachment contextAttachment; + @Mock + private Invoker invoker; + @Mock + private Invocation invocation; + @Mock + private MethodInterceptResult methodInterceptResult; + @Mock + private Result result; + + private Object[] allArguments; + private Class[] argumentTypes; + + @Before + public void setUp() throws Exception { + dubboInterceptor = new DubboInterceptor(); + + PowerMockito.mockStatic(RpcContext.class); + URL url = URL.valueOf("dubbo://127.0.0.1:20880/org.apache.skywalking.apm.test.TestDubboService"); + when(invoker.getUrl()).thenReturn(url); + when(invocation.getMethodName()).thenReturn("test"); + when(invocation.getParameterTypes()).thenReturn(new Class[] {String.class}); + when(invocation.getArguments()).thenReturn(new Object[] {"abc"}); + when(RpcContext.getServiceContext()).thenReturn(serviceContext); + when(serviceContext.getUrl()).thenReturn(null); + when(serviceContext.getConsumerUrl()).thenReturn(url); + when(RpcContext.getClientAttachment()).thenReturn(contextAttachment); + + allArguments = new Object[] { + invoker, + invocation + }; + argumentTypes = new Class[] { + invoker.getClass(), + invocation.getClass() + }; + Config.Agent.SERVICE_NAME = "DubboTestCases-APP"; + } + + @Test + public void testServiceFromPlugin() { + PluginBootService service = ServiceManager.INSTANCE.findService(PluginBootService.class); + + Assert.notNull(service); + } + + @Test + public void testServiceOverrideFromPlugin() { + ContextManagerExtendService service = ServiceManager.INSTANCE.findService(ContextManagerExtendService.class); + + Assert.isInstanceOf(ContextManagerExtendOverrideService.class, service); + } + + @Test + public void testConsumerWithAttachment() throws Throwable { + dubboInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); + dubboInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, result); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + assertThat(spans.size(), is(1)); + assertConsumerSpan(spans.get(0)); + } + + @Test + public void testConsumerWithException() throws Throwable { + dubboInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); + dubboInterceptor.handleMethodException( + enhancedInstance, null, allArguments, argumentTypes, new RuntimeException()); + dubboInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, result); + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + assertConsumerTraceSegmentInErrorCase(traceSegment); + } + + @Test + public void testConsumerWithResultHasException() throws Throwable { + when(result.getException()).thenReturn(new RuntimeException()); + + dubboInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); + dubboInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, result); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + assertConsumerTraceSegmentInErrorCase(traceSegment); + } + + @Test + public void testProviderWithAttachment() throws Throwable { + when(serviceContext.getUrl()).thenReturn( + URL.valueOf("dubbo://127.0.0.1:20880/org.apache.skywalking.apm.test.TestDubboService")); + when(serviceContext.getConsumerUrl()).thenReturn(null); + when(RpcContext.getServerAttachment()).thenReturn(contextAttachment); + when(contextAttachment.getAttachment( + SW8CarrierItem.HEADER_NAME)).thenReturn( + "1-My40LjU=-MS4yLjM=-3-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA="); + + dubboInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); + dubboInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, result); + assertProvider(); + } + + private void assertConsumerTraceSegmentInErrorCase(TraceSegment traceSegment) { + List spans = SegmentHelper.getSpans(traceSegment); + assertThat(spans.size(), is(1)); + assertConsumerSpan(spans.get(0)); + AbstractTracingSpan span = spans.get(0); + assertThat(SpanHelper.getLogs(span).size(), is(1)); + assertErrorLog(SpanHelper.getLogs(span).get(0)); + } + + private void assertErrorLog(LogDataEntity logData) { + assertThat(logData.getLogs().size(), is(4)); + assertThat(logData.getLogs().get(0).getValue(), CoreMatchers.is("error")); + assertThat(logData.getLogs().get(1).getValue(), CoreMatchers.is(RuntimeException.class.getName())); + assertNull(logData.getLogs().get(2).getValue()); + } + + private void assertProvider() { + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + assertThat(SegmentHelper.getSpans(traceSegment).size(), is(1)); + assertProviderSpan(SegmentHelper.getSpans(traceSegment).get(0)); + assertTraceSegmentRef(traceSegment.getRef()); + } + + private void assertTraceSegmentRef(TraceSegmentRef actual) { + assertThat(SegmentRefHelper.getSpanId(actual), is(3)); + assertThat(SegmentRefHelper.getParentServiceInstance(actual), is("instance")); + assertThat(SegmentRefHelper.getTraceSegmentId(actual), is("3.4.5")); + } + + private void assertProviderSpan(AbstractTracingSpan span) { + assertCommonsAttribute(span); + assertTrue(span.isEntry()); + } + + private void assertConsumerSpan(AbstractTracingSpan span) { + assertCommonsAttribute(span); + assertTrue(span.isExit()); + } + + private void assertCommonsAttribute(AbstractTracingSpan span) { + List tags = SpanHelper.getTags(span); + assertThat(tags.size(), is(1)); + assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.RPC_FRAMEWORK)); + assertThat(SpanHelper.getComponentId(span), is(3)); + assertThat( + tags.get(0) + .getValue(), + is("dubbo://127.0.0.1:20880/org.apache.skywalking.apm.test.TestDubboService.test(String)") + ); + assertThat(span.getOperationName(), is("org.apache.skywalking.apm.test.TestDubboService.test(String)")); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/dubbo3/PluginBootService.java b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/dubbo3/PluginBootService.java new file mode 100644 index 0000000000..bbe1ee9da4 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/dubbo3/PluginBootService.java @@ -0,0 +1,43 @@ +/* + * 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.dubbo3; + +import org.apache.skywalking.apm.agent.core.boot.BootService; + +public class PluginBootService implements BootService { + @Override + public void prepare() throws Throwable { + + } + + @Override + public void boot() throws Throwable { + + } + + @Override + public void onComplete() throws Throwable { + + } + + @Override + public void shutdown() throws Throwable { + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/resources/META-INF/services/org.apache.skywalking.apm.agent.core.boot.BootService b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/resources/META-INF/services/org.apache.skywalking.apm.agent.core.boot.BootService new file mode 100644 index 0000000000..74e463dad8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/src/test/resources/META-INF/services/org.apache.skywalking.apm.agent.core.boot.BootService @@ -0,0 +1,20 @@ +# +# 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. +# +# + +org.apache.skywalking.apm.plugin.dubbo3.PluginBootService +org.apache.skywalking.apm.plugin.dubbo3.ContextManagerExtendOverrideService diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml index 2dd47aae4d..0d9f918be5 100644 --- a/apm-sniffer/apm-sdk-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pom.xml @@ -75,6 +75,8 @@ canal-1.x-plugin dubbo-2.7.x-plugin dubbo-2.7.x-conflict-patch + dubbo-3.x-plugin + dubbo-3.x-conflict-patch vertx-plugins resteasy-plugin solrj-7.x-plugin 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 0dbcd9bc5c..9ebd2d4ab6 100644 --- a/docs/en/setup/service-agent/java-agent/Plugin-list.md +++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md @@ -12,6 +12,8 @@ - dbcp-2.x - druid-1.x - dubbo +- dubbo-2.7.x +- dubbo-3.x - ehcache-2.x - elastic-job-2.x - elasticjob-3.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 7c198d145d..6e1f93963f 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -46,7 +46,7 @@ metrics based on the tracing data. * RPC Frameworks * [Dubbo](https://github.com/alibaba/dubbo) 2.5.4 -> 2.6.0 * [Dubbox](https://github.com/dangdangdotcom/dubbox) 2.8.4 - * [Apache Dubbo](https://github.com/apache/dubbo) 2.7.0 + * [Apache Dubbo](https://github.com/apache/dubbo) 2.7.x -> 3.x * [Motan](https://github.com/weibocom/motan) 0.2.x -> 1.1.0 * [gRPC](https://github.com/grpc/grpc-java) 1.x * [Apache ServiceComb Java Chassis](https://github.com/apache/servicecomb-java-chassis) 0.1 -> 0.5,1.x diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/bin/startup.sh b/test/plugin/scenarios/dubbo-3.x-scenario/bin/startup.sh new file mode 100644 index 0000000000..fa89ff14c3 --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/bin/startup.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# 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. + +home="$(cd "$(dirname $0)"; pwd)" + +java -Dskywalking.plugin.dubbo.collect_consumer_arguments=true \ +-Dskywalking.plugin.dubbo.collect_provider_arguments=true -jar ${agent_opts} ${home}/../libs/dubbo-3.x-scenario.jar & diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/dubbo-3.x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..609c17d546 --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/config/expectedData.yaml @@ -0,0 +1,70 @@ +# 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: dubbo-3.x-scenario + segmentSize: ge 3 + segments: + - segmentId: not null + spans: + - operationName: org.apache.skywalking.apm.testcase.dubbo3.services.GreetService.doBusiness(String) + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 3 + isError: false + spanType: Entry + peer: not null + tags: + - {key: url, value: not null} + - {key: arguments, value: helloWorld} + refs: + - {parentEndpoint: GET:/dubbo-3.x-scenario/case/dubbo, networkAddress: not null, + refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: dubbo-3.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: org.apache.skywalking.apm.testcase.dubbo3.services.GreetService.doBusiness(String) + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 3 + isError: false + spanType: Exit + peer: not null + tags: + - {key: url, value: not null} + - {key: arguments, value: helloWorld} + skipAnalysis: 'false' + - operationName: GET:/dubbo-3.x-scenario/case/dubbo + 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/dubbo-3.x-scenario/case/dubbo'} + - {key: http.method, value: GET} + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/configuration.yml b/test/plugin/scenarios/dubbo-3.x-scenario/configuration.yml new file mode 100644 index 0000000000..23dccce063 --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/configuration.yml @@ -0,0 +1,20 @@ +# 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: jvm +entryService: http://localhost:8080/dubbo-3.x-scenario/case/dubbo +healthCheck: http://localhost:8080/dubbo-3.x-scenario/case/healthCheck +startScript: ./bin/startup.sh diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/pom.xml b/test/plugin/scenarios/dubbo-3.x-scenario/pom.xml new file mode 100644 index 0000000000..0154f35ca8 --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/pom.xml @@ -0,0 +1,115 @@ + + + + 4.0.0 + + org.apache.skywalking + dubbo-3.x-scenario + 5.0.0 + + + UTF-8 + 1.8 + 2.1.6.RELEASE + + dubbo + 3.0.0 + + + skywalking-dubbo-3.x-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.apache.dubbo + dubbo + ${test.framework.version} + + + org.apache.dubbo + dubbo-dependencies-zookeeper + ${test.framework.version} + pom + + + + + dubbo-3.x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + maven-compiler-plugin + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + + diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..33054a735e --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ./target/dubbo-3.x-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/Application.java b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/Application.java new file mode 100644 index 0000000000..238c416ecb --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/Application.java @@ -0,0 +1,77 @@ +/* + * 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.testcase.dubbo3; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ServiceConfig; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.skywalking.apm.testcase.dubbo3.services.GreetService; +import org.apache.skywalking.apm.testcase.dubbo3.services.impl.GreetServiceImpl; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + new EmbeddedZooKeeper(2181, false).start(); + SpringApplication.run(Application.class, args); + } + + @Configuration + public static class DubboConfiguration { + + private ApplicationConfig applicationConfig = new ApplicationConfig(Application.class.getSimpleName()); + + private RegistryConfig registryConfig = new RegistryConfig("zookeeper://127.0.0.1:2181"); + + private ProtocolConfig protocolConfig = new ProtocolConfig("dubbo", 20080); + + public DubboConfiguration() { + ApplicationModel.getConfigManager().setApplication(applicationConfig); + } + + @Bean(destroyMethod = "unexport") + public ServiceConfig service() { + ServiceConfig serviceConfig = new ServiceConfig<>(); + serviceConfig.setRegistry(registryConfig); + serviceConfig.setProtocol(protocolConfig); + serviceConfig.setInterface(GreetService.class); + serviceConfig.setRef(new GreetServiceImpl()); + serviceConfig.setTimeout(5000); + serviceConfig.export(); + return serviceConfig; + } + + @Bean(destroyMethod = "destroy") + public ReferenceConfig reference() { + ReferenceConfig referenceConfig = new ReferenceConfig<>(); + referenceConfig.setRegistry(registryConfig); + referenceConfig.setInterface(GreetService.class); + referenceConfig.setScope("remote"); + return referenceConfig; + } + } + +} diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/EmbeddedZooKeeper.java b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/EmbeddedZooKeeper.java new file mode 100644 index 0000000000..a5e925ae7c --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/EmbeddedZooKeeper.java @@ -0,0 +1,265 @@ +/* + * 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. + * + */ + +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.testcase.dubbo3; + +import org.apache.zookeeper.server.ServerConfig; +import org.apache.zookeeper.server.ZooKeeperServerMain; +import org.apache.zookeeper.server.quorum.QuorumPeerConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.SmartLifecycle; +import org.springframework.util.ErrorHandler; +import org.springframework.util.SocketUtils; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.Properties; +import java.util.UUID; + +/** + * from: https://github.com/spring-projects/spring-xd/blob/v1.3.1.RELEASE/spring-xd-dirt/src/main/java/org/springframework/xd/dirt/zookeeper/ZooKeeperUtils.java + *

+ * Helper class to start an embedded instance of standalone (non clustered) ZooKeeper. + *

+ * NOTE: at least an external standalone server (if not an ensemble) are recommended, even for {@link + * org.springframework.xd.dirt.server.singlenode.SingleNodeApplication} + */ +public class EmbeddedZooKeeper implements SmartLifecycle { + + /** + * Logger. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(EmbeddedZooKeeper.class); + + /** + * ZooKeeper client port. This will be determined dynamically upon startup. + */ + private final int clientPort; + + /** + * Whether to auto-start. Default is true. + */ + private boolean autoStartup = true; + + /** + * Lifecycle phase. Default is 0. + */ + private int phase = 0; + + /** + * Thread for running the ZooKeeper server. + */ + private volatile Thread zkServerThread; + + /** + * ZooKeeper server. + */ + private volatile ZooKeeperServerMain zkServer; + + /** + * {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread. + */ + private ErrorHandler errorHandler; + + private boolean daemon = true; + + /** + * Construct an EmbeddedZooKeeper with a random port. + */ + public EmbeddedZooKeeper() { + clientPort = SocketUtils.findAvailableTcpPort(); + } + + /** + * Construct an EmbeddedZooKeeper with the provided port. + * + * @param clientPort port for ZooKeeper server to bind to + */ + public EmbeddedZooKeeper(int clientPort, boolean daemon) { + this.clientPort = clientPort; + this.daemon = daemon; + } + + /** + * Returns the port that clients should use to connect to this embedded server. + * + * @return dynamically determined client port + */ + public int getClientPort() { + return this.clientPort; + } + + /** + * Specify whether to start automatically. Default is true. + * + * @param autoStartup whether to start automatically + */ + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAutoStartup() { + return this.autoStartup; + } + + /** + * Specify the lifecycle phase for the embedded server. + * + * @param phase the lifecycle phase + */ + public void setPhase(int phase) { + this.phase = phase; + } + + /** + * {@inheritDoc} + */ + @Override + public int getPhase() { + return this.phase; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunning() { + return zkServerThread != null; + } + + /** + * Start the ZooKeeper server in a background thread. + *

+ * Register an error handler via {@link #setErrorHandler} in order to handle any exceptions thrown during startup or + * execution. + */ + @Override + public synchronized void start() { + if (zkServerThread == null) { + zkServerThread = new Thread(new ServerRunnable(), "ZooKeeper Server Starter"); + zkServerThread.setDaemon(daemon); + zkServerThread.start(); + } + } + + /** + * Shutdown the ZooKeeper server. + */ + @Override + public synchronized void stop() { + if (zkServerThread != null) { + // The shutdown method is protected...thus this hack to invoke it. + // This will log an exception on shutdown; see + // https://issues.apache.org/jira/browse/ZOOKEEPER-1873 for details. + try { + Method shutdown = ZooKeeperServerMain.class.getDeclaredMethod("shutdown"); + shutdown.setAccessible(true); + shutdown.invoke(zkServer); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // It is expected that the thread will exit after + // the server is shutdown; this will block until + // the shutdown is complete. + try { + zkServerThread.join(5000); + zkServerThread = null; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.warn("Interrupted while waiting for embedded ZooKeeper to exit"); + // abandoning zk thread + zkServerThread = null; + } + } + } + + /** + * Stop the server if running and invoke the callback when complete. + */ + @Override + public void stop(Runnable callback) { + stop(); + callback.run(); + } + + /** + * Provide an {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread. If none + * is provided, only error-level logging will occur. + * + * @param errorHandler the {@link ErrorHandler} to be invoked + */ + public void setErrorHandler(ErrorHandler errorHandler) { + this.errorHandler = errorHandler; + } + + /** + * Runnable implementation that starts the ZooKeeper server. + */ + private class ServerRunnable implements Runnable { + + @Override + public void run() { + try { + Properties properties = new Properties(); + File file = new File(System.getProperty("java.io.tmpdir") + + File.separator + UUID.randomUUID()); + file.deleteOnExit(); + properties.setProperty("dataDir", file.getAbsolutePath()); + properties.setProperty("clientPort", String.valueOf(clientPort)); + + QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); + quorumPeerConfig.parseProperties(properties); + + zkServer = new ZooKeeperServerMain(); + ServerConfig configuration = new ServerConfig(); + configuration.readFrom(quorumPeerConfig); + + zkServer.runFromConfig(configuration); + } catch (Exception e) { + if (errorHandler != null) { + errorHandler.handleError(e); + } else { + LOGGER.error("Exception running embedded ZooKeeper", e); + } + } + } + } + +} diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/controller/CaseController.java b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/controller/CaseController.java new file mode 100644 index 0000000000..d61172f253 --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/controller/CaseController.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.testcase.dubbo3.controller; + +import org.apache.dubbo.config.ReferenceConfig; +import org.apache.skywalking.apm.testcase.dubbo3.services.GreetService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/case") +public class CaseController { + + private static final String SUCCESS = "Success"; + + @Autowired + private ReferenceConfig referenceConfig; + + @RequestMapping("/healthCheck") + @ResponseBody + public String healthCheck() { + return SUCCESS; + } + + @RequestMapping("/dubbo") + @ResponseBody + public String dubbo() { + GreetService greetService = referenceConfig.get(); + greetService.doBusiness("helloWorld"); + return SUCCESS; + } +} diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/services/GreetService.java b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/services/GreetService.java new file mode 100644 index 0000000000..55861a0fc9 --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/services/GreetService.java @@ -0,0 +1,23 @@ +/* + * 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.testcase.dubbo3.services; + +public interface GreetService { + String doBusiness(String s); +} diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/services/impl/GreetServiceImpl.java b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/services/impl/GreetServiceImpl.java new file mode 100644 index 0000000000..472957cefa --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/dubbo3/services/impl/GreetServiceImpl.java @@ -0,0 +1,29 @@ +/* + * 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.testcase.dubbo3.services.impl; + +import org.apache.skywalking.apm.testcase.dubbo3.services.GreetService; + +public class GreetServiceImpl implements GreetService { + + @Override + public String doBusiness(String s) { + return "{name:'" + s + "'}"; + } +} diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/resources/application.yaml new file mode 100644 index 0000000000..1481d38f02 --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/src/main/resources/application.yaml @@ -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. + +server: + port: 8080 + servlet: + context-path: /dubbo-3.x-scenario + diff --git a/test/plugin/scenarios/dubbo-3.x-scenario/support-version.list b/test/plugin/scenarios/dubbo-3.x-scenario/support-version.list new file mode 100644 index 0000000000..48d725e191 --- /dev/null +++ b/test/plugin/scenarios/dubbo-3.x-scenario/support-version.list @@ -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. + +3.0.4