diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java index 61e99022963..2047642f24a 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java @@ -17,8 +17,8 @@ package org.apache.dubbo.rpc.protocol.dubbo.telnet; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; @@ -42,90 +42,13 @@ * InvokeTelnetHandler */ @Activate -@Help(parameter = "[service.]method(args) [-p parameter classes]", summary = "Invoke the service method.", +@Help(parameter = "[service.]method(args) ", summary = "Invoke the service method.", detail = "Invoke the service method.") public class InvokeTelnetHandler implements TelnetHandler { - private static Method findMethod(List methods, String method, List args, - Class[] paramTypes) { - for (ProviderMethodModel model : methods) { - Method m = model.getMethod(); - if (isMatch(m, args, paramTypes, method)) { - return m; - } - } - return null; - } - - private static boolean isMatch(Method method, List args, Class[] paramClasses, String lookupMethodName) { - if (!method.getName().equals(lookupMethodName)) { - return false; - } - - Class types[] = method.getParameterTypes(); - if (types.length != args.size()) { - return false; - } - for (int i = 0; i < types.length; i++) { - Class type = types[i]; - Object arg = args.get(i); - - if (paramClasses != null && type != paramClasses[i]) { - return false; - } - if (arg == null) { - // if the type is primitive, the method to invoke will cause NullPointerException definitely - // so we can offer a specified error message to the invoker in advance and avoid unnecessary invoking - if (type.isPrimitive()) { - throw new NullPointerException(String.format("The type of No.%d parameter is primitive(%s), " + - "but the value passed is null.", i + 1, type.getName())); - } - - // if the type is not primitive, we choose to believe what the invoker want is a null value - continue; - } - - if (ReflectUtils.isPrimitive(arg.getClass())) { - // allow string arg to enum type, @see PojoUtils.realize0() - if (arg instanceof String && type.isEnum()) { - continue; - } - - if (!ReflectUtils.isPrimitive(type)) { - return false; - } - - if (!ReflectUtils.isCompatible(type, arg)) { - return false; - } - } else if (arg instanceof Map) { - String name = (String) ((Map) arg).get("class"); - if (StringUtils.isNotEmpty(name)) { - Class cls = ReflectUtils.forName(name); - if (!type.isAssignableFrom(cls)) { - return false; - } - } else { - if (arg instanceof JSONObject) { - try { - ((JSONObject) arg).toJavaObject(type); - } catch (Exception ex) { - return false; - } - } - } - } else if (arg instanceof Collection) { - if (!type.isArray() && !type.isAssignableFrom(arg.getClass())) { - return false; - } - } else { - if (!type.isAssignableFrom(arg.getClass())) { - return false; - } - } - } - return true; - } + public static final String INVOKE_MESSAGE_KEY = "telnet.invoke.method.message"; + public static final String INVOKE_METHOD_LIST_KEY = "telnet.invoke.method.list"; + public static final String INVOKE_METHOD_PROVIDER_KEY = "telnet.invoke.method.provider"; @Override @SuppressWarnings("unchecked") @@ -136,33 +59,9 @@ public String telnet(Channel channel, String message) { "invoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})"; } - StringBuilder buf = new StringBuilder(); String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY); - if (!StringUtils.isEmpty(service)) { - buf.append("Use default service ").append(service).append("."); - } int i = message.indexOf("("); - String originalMessage = message; - Class[] paramTypes = null; - if (message.contains("-p")) { - message = originalMessage.substring(0, originalMessage.indexOf("-p")).trim(); - String paramClassesString = originalMessage.substring(originalMessage.indexOf("-p") + 2).trim(); - if (paramClassesString.length() > 0) { - String[] split = paramClassesString.split("\\s+"); - if (split.length > 0) { - paramTypes = new Class[split.length]; - for (int j = 0; j < split.length; j++) { - try { - paramTypes[j] = Class.forName(split[j]); - } catch (ClassNotFoundException e) { - return "Unknown parameter class for name " + split[j]; - } - } - - } - } - } if (i < 0 || !message.endsWith(")")) { return "Invalid parameters, format: service.method(args)"; @@ -182,32 +81,44 @@ public String telnet(Channel channel, String message) { } catch (Throwable t) { return "Invalid json argument, cause: " + t.getMessage(); } - if (paramTypes != null) { - if (paramTypes.length != list.size()) { - return "Parameter's number does not match the number of parameter class"; - } - List listOfActualClass = new ArrayList<>(list.size()); - for (int ii = 0; ii < list.size(); ii++) { - if (list.get(ii) instanceof JSONObject) { - JSONObject jsonObject = (JSONObject) list.get(ii); - listOfActualClass.add(jsonObject.toJavaObject(paramTypes[ii])); - } else { - listOfActualClass.add(list.get(ii)); - } - } - list = listOfActualClass; - } - + StringBuilder buf = new StringBuilder(); Method invokeMethod = null; ProviderModel selectedProvider = null; - for (ProviderModel provider : ApplicationModel.allProviderModels()) { - if (isServiceMatch(service, provider)) { - invokeMethod = findMethod(provider.getAllMethods(), method, list, paramTypes); - selectedProvider = provider; - break; + if (isInvokedSelectCommand(channel)) { + selectedProvider = (ProviderModel) channel.getAttribute(INVOKE_METHOD_PROVIDER_KEY); + invokeMethod = (Method) channel.getAttribute(SelectTelnetHandler.SELECT_METHOD_KEY); + } else { + for (ProviderModel provider : ApplicationModel.allProviderModels()) { + if (isServiceMatch(service, provider)) { + selectedProvider = provider; + List methodList = findSameSignatureMethod(provider.getAllMethods(), method, list); + if (CollectionUtils.isNotEmpty(methodList)) { + if (methodList.size() == 1) { + invokeMethod = methodList.get(0); + } else { + List matchMethods = findMatchMethods(methodList, list); + if (CollectionUtils.isNotEmpty(matchMethods)) { + if (matchMethods.size() == 1) { + invokeMethod = matchMethods.get(0); + } else { //exist overridden method + channel.setAttribute(INVOKE_METHOD_PROVIDER_KEY, provider); + channel.setAttribute(INVOKE_METHOD_LIST_KEY, matchMethods); + channel.setAttribute(INVOKE_MESSAGE_KEY, message); + printSelectMessage(buf, matchMethods); + return buf.toString(); + } + } + } + } + break; + } } } + + if (!StringUtils.isEmpty(service)) { + buf.append("Use default service ").append(service).append("."); + } if (selectedProvider != null) { if (invokeMethod != null) { try { @@ -240,10 +151,111 @@ public String telnet(Channel channel, String message) { return buf.toString(); } + private boolean isServiceMatch(String service, ProviderModel provider) { return provider.getServiceName().equalsIgnoreCase(service) || provider.getServiceInterfaceClass().getSimpleName().equalsIgnoreCase(service) || provider.getServiceInterfaceClass().getName().equalsIgnoreCase(service) || StringUtils.isEmpty(service); } + + private List findSameSignatureMethod(List methods, String lookupMethodName, List args) { + List sameSignatureMethods = new ArrayList<>(); + for (ProviderMethodModel model : methods) { + Method method = model.getMethod(); + if (method.getName().equals(lookupMethodName) && method.getParameterTypes().length == args.size()) { + sameSignatureMethods.add(method); + } + } + return sameSignatureMethods; + } + + private List findMatchMethods(List methods, List args) { + List matchMethod = new ArrayList<>(); + for (Method method : methods) { + if (isMatch(method, args)) { + matchMethod.add(method); + } + } + return matchMethod; + } + + private static boolean isMatch(Method method, List args) { + Class[] types = method.getParameterTypes(); + if (types.length != args.size()) { + return false; + } + for (int i = 0; i < types.length; i++) { + Class type = types[i]; + Object arg = args.get(i); + + if (arg == null) { + if (type.isPrimitive()) { + return false; + } + + // if the type is not primitive, we choose to believe what the invoker want is a null value + continue; + } + + if (ReflectUtils.isPrimitive(arg.getClass())) { + // allow string arg to enum type, @see PojoUtils.realize0() + if (arg instanceof String && type.isEnum()) { + continue; + } + + if (!ReflectUtils.isPrimitive(type)) { + return false; + } + + if (!ReflectUtils.isCompatible(type, arg)) { + return false; + } + } else if (arg instanceof Map) { + String name = (String) ((Map) arg).get("class"); + if (StringUtils.isNotEmpty(name)) { + Class cls = ReflectUtils.forName(name); + if (!type.isAssignableFrom(cls)) { + return false; + } + } else { + return true; + } + } else if (arg instanceof Collection) { + if (!type.isArray() && !type.isAssignableFrom(arg.getClass())) { + return false; + } + } else { + if (!type.isAssignableFrom(arg.getClass())) { + return false; + } + } + } + return true; + } + + private void printSelectMessage(StringBuilder buf, List methods) { + buf.append("Methods:\r\n"); + for (int i = 0; i < methods.size(); i++) { + Method method = methods.get(i); + buf.append((i + 1) + ". " + method.getName() + "("); + Class[] parameterTypes = method.getParameterTypes(); + for (int n = 0; n < parameterTypes.length; n++) { + buf.append(parameterTypes[n].getSimpleName()); + if (n != parameterTypes.length - 1) { + buf.append(","); + } + } + buf.append(")\r\n"); + } + buf.append("Please use the select command to select the method you want to invoke. eg: select 1"); + } + + private boolean isInvokedSelectCommand(Channel channel) { + if (channel.hasAttribute(SelectTelnetHandler.SELECT_KEY)) { + channel.removeAttribute(SelectTelnetHandler.SELECT_KEY); + return true; + } + return false; + } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandler.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandler.java new file mode 100644 index 00000000000..ed76ebb3913 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandler.java @@ -0,0 +1,60 @@ +/* + * 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.dubbo.rpc.protocol.dubbo.telnet; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.telnet.TelnetHandler; +import org.apache.dubbo.remoting.telnet.support.Help; + +import java.lang.reflect.Method; +import java.util.List; + +/** + * SelectTelnetHandler + */ +@Activate +@Help(parameter = "[index]", summary = "Select the index of the method you want to invoke.", + detail = "Select the index of the method you want to invoke.") +public class SelectTelnetHandler implements TelnetHandler { + public static final String SELECT_METHOD_KEY = "telnet.select.method"; + public static final String SELECT_KEY = "telnet.select"; + + private InvokeTelnetHandler invokeTelnetHandler = new InvokeTelnetHandler(); + + @Override + @SuppressWarnings("unchecked") + public String telnet(Channel channel, String message) { + if (message == null || message.length() == 0) { + return "Please input the index of the method you want to invoke, eg: \r\n select 1"; + } + List methodList = (List) channel.getAttribute(InvokeTelnetHandler.INVOKE_METHOD_LIST_KEY); + if (CollectionUtils.isEmpty(methodList)) { + return "Please use the invoke command first."; + } + if (!StringUtils.isInteger(message) || Integer.parseInt(message) < 1 || Integer.parseInt(message) > methodList.size()) { + return "Illegal index ,please input select 1~" + methodList.size(); + } + Method method = methodList.get(Integer.parseInt(message)); + channel.setAttribute(SELECT_METHOD_KEY, method); + channel.setAttribute(SELECT_KEY, Boolean.TRUE); + String invokeMessage = (String) channel.getAttribute(InvokeTelnetHandler.INVOKE_MESSAGE_KEY); + return invokeTelnetHandler.telnet(channel, invokeMessage); + } +} diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler index ad5b55f9e04..ef32515fe7a 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler @@ -4,4 +4,5 @@ cd=org.apache.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler pwd=org.apache.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler invoke=org.apache.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler trace=org.apache.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler -count=org.apache.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler \ No newline at end of file +count=org.apache.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler +select=org.apache.dubbo.rpc.protocol.dubbo.telnet.SelectTelnetHandler \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java index f1752be8ef7..4512d29a6a8 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java @@ -16,8 +16,10 @@ */ package org.apache.dubbo.rpc.protocol.dubbo.telnet; +import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.rpc.model.ApplicationModel; @@ -25,23 +27,27 @@ import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService; import org.apache.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl; import org.apache.dubbo.rpc.protocol.dubbo.support.ProtocolUtils; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.net.InetSocketAddress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; /** - * CountTelnetHandlerTest.java + * InvokeTelnetHandlerTest.java */ public class InvokerTelnetHandlerTest { private static TelnetHandler invoke = new InvokeTelnetHandler(); + private static TelnetHandler select = new SelectTelnetHandler(); private Channel mockChannel; @BeforeEach @@ -56,176 +62,98 @@ public void after() { @SuppressWarnings("unchecked") @Test - public void testInvokeDefaultSService() throws RemotingException { + public void testInvokeDefaultService() throws RemotingException { mockChannel = mock(Channel.class); - given(mockChannel.getAttribute("telnet.service")).willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); + given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName()); given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); - String result = invoke.telnet(mockChannel, "DemoService.echo(\"ok\")"); + String result = invoke.telnet(mockChannel, "echo(\"ok\")"); assertTrue(result.contains("result: \"ok\"")); } @SuppressWarnings("unchecked") @Test - public void testInvokeByPassingNullValue() throws RemotingException { - mockChannel = mock(Channel.class); - given(mockChannel.getAttribute("telnet.service")).willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); - given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); - given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); - - // pass null value to parameter of primitive type - try { - invoke.telnet(mockChannel, "DemoService.add(null, 2)"); - fail("It should cause a NullPointerException by the above code."); - } catch (NullPointerException ex) { - String message = ex.getMessage(); - assertEquals("The type of No.1 parameter is primitive(int), but the value passed is null.", message); - } - - try { - invoke.telnet(mockChannel, "DemoService.add(1, null)"); - fail("It should cause a NullPointerException by the above code."); - } catch (NullPointerException ex) { - String message = ex.getMessage(); - assertEquals("The type of No.2 parameter is primitive(long), but the value passed is null.", message); - } - - // pass null value to parameter of object type - try { - invoke.telnet(mockChannel, "DemoService.sayHello(null)"); - } catch (NullPointerException ex) { - fail("It shouldn't cause a NullPointerException by the above code."); - } - } - - @Test - public void testInvokeByPassingEnumValue() throws RemotingException { + public void testInvokeWithSpecifyService() throws RemotingException { mockChannel = mock(Channel.class); given(mockChannel.getAttribute("telnet.service")).willReturn(null); given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); - - String result = invoke.telnet(mockChannel, "getType(\"High\")"); - assertTrue(result.contains("result: \"High\"")); - } - - - @SuppressWarnings("unchecked") - @Test - public void testComplexParamWithoutSpecifyParamType() throws RemotingException { - mockChannel = mock(Channel.class); - given(mockChannel.getAttribute("telnet.service")).willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); - given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); - given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); - // pass json value to parameter of Person type - - String result = invoke.telnet(mockChannel, "DemoService.getPerson({\"name\":\"zhangsan\",\"age\":12,\"class\":\"org.apache.dubbo.rpc.protocol.dubbo.support.Person\"})"); - assertTrue(result.contains("result: 12")); + String result = invoke.telnet(mockChannel, "DemoService.echo(\"ok\")"); + assertTrue(result.contains("result: \"ok\"")); } @SuppressWarnings("unchecked") @Test - public void testComplexParamSpecifyParamType() throws RemotingException { + public void testInvokeByPassingNullValue() { mockChannel = mock(Channel.class); - given(mockChannel.getAttribute("telnet.service")).willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); + given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName()); given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); - - // pass json value to parameter of Person type and specify it's type - // one parameter with type of Person - String result = invoke.telnet(mockChannel, "DemoService.getPerson({\"name\":\"zhangsan\",\"age\":12}) -p org.apache.dubbo.rpc.protocol.dubbo.support.Person"); - assertTrue(result.contains("result: 12")); - - // two parameter with type of Person - result = invoke.telnet(mockChannel, "DemoService.getPerson({\"name\":\"zhangsan\",\"age\":12},{\"name\":\"lisi\",\"age\":12}) " + - "-p org.apache.dubbo.rpc.protocol.dubbo.support.Person " + - "org.apache.dubbo.rpc.protocol.dubbo.support.Person"); - assertTrue(result.contains("result: 24")); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + try { + invoke.telnet(mockChannel, "sayHello(null)"); + } catch (Exception ex) { + assertTrue(ex instanceof NullPointerException); + } } - @SuppressWarnings("unchecked") @Test - public void testComplexParamSpecifyWrongParamType() throws RemotingException { + public void testInvokeByPassingEnumValue() throws RemotingException { mockChannel = mock(Channel.class); - given(mockChannel.getAttribute("telnet.service")).willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); + given(mockChannel.getAttribute("telnet.service")).willReturn(null); given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); - - // pass json value to parameter of Person type - // wrong name of parameter class - String result = invoke.telnet(mockChannel, "DemoService.getPerson({\"name\":\"zhangsan\",\"age\":12}) -p wrongType"); - assertEquals("Unknown parameter class for name wrongType", result); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); - // wrong number of parameter class - result = invoke.telnet(mockChannel, "DemoService.getPerson({\"name\":\"zhangsan\",\"age\":12},{\"name\":\"lisi\",\"age\":12}) " + - "-p org.apache.dubbo.rpc.protocol.dubbo.support.Person"); - assertEquals("Parameter's number does not match the number of parameter class", result); + String result = invoke.telnet(mockChannel, "getType(\"High\")"); + assertTrue(result.contains("result: \"High\"")); } @SuppressWarnings("unchecked") @Test - public void testInvokeAutoFindMethod() throws RemotingException { + public void testOverriddenMethodWithSpecifyParamType() throws RemotingException { mockChannel = mock(Channel.class); - given(mockChannel.getAttribute("telnet.service")).willReturn(null); + given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName()); given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); - - String result = invoke.telnet(mockChannel, "echo(\"ok\")"); - assertTrue(result.contains("result: \"ok\"")); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + String result = invoke.telnet(mockChannel, "getPerson({\"name\":\"zhangsan\",\"age\":12,\"class\":\"org.apache.dubbo.rpc.protocol.dubbo.support.Person\"})"); + assertTrue(result.contains("result: 12")); } @Test - public void testInvokeJsonParamMethod() throws RemotingException { - mockChannel = mock(Channel.class); - given(mockChannel.getAttribute("telnet.service")).willReturn(null); + public void testInvokeOverriddenMethodBySelect() throws RemotingException { + //create a real instance to keep the attribute values; + mockChannel = spy(getChannelInstance()); + given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName()); given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); String param = "{\"name\":\"Dubbo\",\"age\":8}"; String result = invoke.telnet(mockChannel, "getPerson(" + param + ")"); + assertTrue(result.contains("Please use the select command to select the method you want to invoke. eg: select 1")); + result = select.telnet(mockChannel, "1"); + //result dependent on method order. assertTrue(result.contains("result: 8") || result.contains("result: \"Dubbo\"")); } - @Test - public void testInvokeSpecifyTypeJsonParamMethod() throws RemotingException { - mockChannel = mock(Channel.class); - given(mockChannel.getAttribute("telnet.service")).willReturn(null); - given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); - given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); - String param = "{\"name\":\"Dubbo\",\"age\":8,\"class\":\"org.apache.dubbo.rpc.protocol.dubbo.support.Man\"}"; - String result = invoke.telnet(mockChannel, "getPerson(" + param + ")"); - assertTrue(result.contains("result: \"Dubbo\"")); - } - @Test public void testInvokeMultiJsonParamMethod() throws RemotingException { mockChannel = mock(Channel.class); @@ -233,8 +161,8 @@ public void testInvokeMultiJsonParamMethod() throws RemotingException { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); String param = "{\"name\":\"Dubbo\",\"age\":8},{\"name\":\"Apache\",\"age\":20}"; String result = invoke.telnet(mockChannel, "getPerson(" + param + ")"); assertTrue(result.contains("result: 28")); @@ -254,8 +182,93 @@ public void testMessageNull() throws RemotingException { public void testInvalidMessage() throws RemotingException { mockChannel = mock(Channel.class); given(mockChannel.getAttribute("telnet.service")).willReturn(null); - String result = invoke.telnet(mockChannel, "("); assertEquals("Invalid parameters, format: service.method(args)", result); } + + private Channel getChannelInstance() { + return new Channel() { + private final Map attributes = new ConcurrentHashMap(); + + @Override + public InetSocketAddress getRemoteAddress() { + return null; + } + + @Override + public boolean isConnected() { + return false; + } + + @Override + public boolean hasAttribute(String key) { + return attributes.containsKey(key); + } + + @Override + public Object getAttribute(String key) { + return attributes.get(key); + } + + @Override + public void setAttribute(String key, Object value) { + if (value == null) { // The null value unallowed in the ConcurrentHashMap. + attributes.remove(key); + } else { + attributes.put(key, value); + } + } + + @Override + public void removeAttribute(String key) { + attributes.remove(key); + } + + + @Override + public URL getUrl() { + return null; + } + + @Override + public ChannelHandler getChannelHandler() { + return null; + } + + @Override + public InetSocketAddress getLocalAddress() { + return null; + } + + @Override + public void send(Object message) throws RemotingException { + + } + + @Override + public void send(Object message, boolean sent) throws RemotingException { + + } + + @Override + public void close() { + + } + + @Override + public void close(int timeout) { + + } + + @Override + public void startClose() { + + } + + @Override + public boolean isClosed() { + return false; + } + }; + } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandlerTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandlerTest.java new file mode 100644 index 00000000000..df8bf470add --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandlerTest.java @@ -0,0 +1,116 @@ +/* + * 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.dubbo.rpc.protocol.dubbo.telnet; + +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.remoting.telnet.TelnetHandler; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ProviderModel; +import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService; +import org.apache.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl; +import org.apache.dubbo.rpc.protocol.dubbo.support.ProtocolUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * SelectTelnetHandlerTest.java + */ +public class SelectTelnetHandlerTest { + + private static TelnetHandler select = new SelectTelnetHandler(); + private Channel mockChannel; + List methods; + + @BeforeEach + public void setup() { + String methodName = "getPerson"; + methods = new ArrayList<>(); + for (Method method : DemoService.class.getMethods()) { + if (method.getName().equals(methodName)) { + methods.add(method); + } + } + + ApplicationModel.reset(); + } + + @AfterEach + public void after() { + ProtocolUtils.closeAll(); + } + + @Test + public void testInvokeWithoutMethodList() throws RemotingException { + mockChannel = mock(Channel.class); + given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName()); + given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); + given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); + + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + + String result = select.telnet(mockChannel, "1"); + assertTrue(result.contains("Please use the invoke command first.")); + } + + @Test + public void testInvokeWithIllegalMessage() throws RemotingException { + mockChannel = mock(Channel.class); + given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName()); + given(mockChannel.getAttribute(InvokeTelnetHandler.INVOKE_METHOD_LIST_KEY)).willReturn(methods); + given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); + given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); + + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + + String result = select.telnet(mockChannel, "index"); + assertTrue(result.contains("Illegal index ,please input select 1")); + + result = select.telnet(mockChannel, "0"); + assertTrue(result.contains("Illegal index ,please input select 1")); + + result = select.telnet(mockChannel, "1000"); + assertTrue(result.contains("Illegal index ,please input select 1")); + } + + @Test + public void testInvokeWithNull() throws RemotingException { + mockChannel = mock(Channel.class); + given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName()); + given(mockChannel.getAttribute(InvokeTelnetHandler.INVOKE_METHOD_LIST_KEY)).willReturn(methods); + given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); + given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); + + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + + String result = select.telnet(mockChannel, null); + assertTrue(result.contains("Please input the index of the method you want to invoke")); + } +}