diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java index 26335f0b41d..84325918f81 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java @@ -59,9 +59,9 @@ public String telnet(Channel channel, String message) { Help help = handler.getClass().getAnnotation(Help.class); List row = new ArrayList(); String parameter = " " + extensionLoader.getExtensionName(handler) + " " + (help != null ? help.parameter().replace("\r\n", " ").replace("\n", " ") : ""); - row.add(parameter.length() > 50 ? parameter.substring(0, 50) + "..." : parameter); + row.add(parameter.length() > 55 ? parameter.substring(0, 55) + "..." : parameter); String summary = help != null ? help.summary().replace("\r\n", " ").replace("\n", " ") : ""; - row.add(summary.length() > 50 ? summary.substring(0, 50) + "..." : summary); + row.add(summary.length() > 55 ? summary.substring(0, 55) + "..." : summary); table.add(row); } } 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 caa9f9a7f8d..a472fb723fc 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 @@ -30,8 +30,10 @@ import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -40,21 +42,21 @@ * InvokeTelnetHandler */ @Activate -@Help(parameter = "[service.]method(args)", summary = "Invoke the service method.", detail = "Invoke the service method.") +@Help(parameter = "[service.]method(args) [-p parameter classes]", summary = "Invoke the service method.", detail = "Invoke the service method.") public class InvokeTelnetHandler implements TelnetHandler { - private static Method findMethod(Exporter exporter, String method, List args) { + private static Method findMethod(Exporter exporter, String method, List args, Class[] paramClases) { Invoker invoker = exporter.getInvoker(); Method[] methods = invoker.getInterface().getMethods(); for (Method m : methods) { - if (m.getName().equals(method) && isMatch(m.getParameterTypes(), args)) { + if (m.getName().equals(method) && isMatch(m.getParameterTypes(), args, paramClases)) { return m; } } return null; } - private static boolean isMatch(Class[] types, List args) { + private static boolean isMatch(Class[] types, List args, Class[] paramClasses) { if (types.length != args.size()) { return false; } @@ -62,6 +64,10 @@ private static boolean isMatch(Class[] types, List args) { 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 @@ -83,8 +89,8 @@ private static boolean isMatch(Class[] types, List args) { if (!ReflectUtils.isPrimitive(type)) { return false; } - Class boxedType = ReflectUtils.getBoxedClass(type); - if (boxedType != arg.getClass()) { + + if (!ReflectUtils.isCompatible(type, arg)) { return false; } } else if (arg instanceof Map) { @@ -121,6 +127,26 @@ public String telnet(Channel channel, String message) { buf.append("Use default service " + service + ".\r\n"); } int i = message.indexOf("("); + String originalMessage = message; + Class[] paramClasses = 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) { + paramClasses = new Class[split.length]; + for (int j = 0; j < split.length; j++) { + try { + paramClasses[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)"; } @@ -137,11 +163,26 @@ public String telnet(Channel channel, String message) { } catch (Throwable t) { return "Invalid json argument, cause: " + t.getMessage(); } + if (paramClasses != null) { + if (paramClasses.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(paramClasses[ii])); + } else { + listOfActualClass.add(list.get(ii)); + } + } + list = listOfActualClass; + } Invoker invoker = null; Method invokeMethod = null; for (Exporter exporter : DubboProtocol.getDubboProtocol().getExporters()) { if (service == null || service.length() == 0) { - invokeMethod = findMethod(exporter, method, list); + invokeMethod = findMethod(exporter, method, list, paramClasses); if (invokeMethod != null) { invoker = exporter.getInvoker(); break; @@ -150,7 +191,7 @@ public String telnet(Channel channel, String message) { if (service.equals(exporter.getInvoker().getInterface().getSimpleName()) || service.equals(exporter.getInvoker().getInterface().getName()) || service.equals(exporter.getInvoker().getUrl().getPath())) { - invokeMethod = findMethod(exporter, method, list); + invokeMethod = findMethod(exporter, method, list, paramClasses); invoker = exporter.getInvoker(); break; } @@ -179,5 +220,4 @@ public String telnet(Channel channel, String message) { } return buf.toString(); } - } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java index db85cc51b3d..fe777e37de2 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java @@ -59,4 +59,8 @@ public interface DemoService { long add(int a, long b); + int getPerson(Person person); + + int getPerson(Person person1, Person perso2); + } \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java index ed38344a324..c1b03f64e25 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java @@ -107,4 +107,14 @@ public long add(int a, long b) { return a + b; } + @Override + public int getPerson(Person person) { + return 1; + } + + @Override + public int getPerson(Person person1, Person perso2) { + return 2; + } + } \ 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 dc63c8b8b53..a28bc99ac22 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 @@ -125,6 +125,78 @@ public void testInvokeByPassingEnumValue() throws RemotingException { } + @SuppressWarnings("unchecked") + @Test + public void testComplexParamWithoutSpecifyParamType() throws RemotingException { + mockInvoker = mock(Invoker.class); + given(mockInvoker.getInterface()).willReturn(DemoService.class); + given(mockInvoker.getUrl()).willReturn(URL.valueOf("dubbo://127.0.0.1:20886/demo")); + given(mockInvoker.invoke(any(Invocation.class))).willReturn(new RpcResult("ok")); + 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")); + + DubboProtocol.getDubboProtocol().export(mockInvoker); + + // pass json value to parameter of Person type + + String result = invoke.telnet(mockChannel, "DemoService.getPerson({\"name\":\"zhangsan\",\"age\":12})"); + assertTrue(result.contains("No such method getPerson in service DemoService")); + } + + @SuppressWarnings("unchecked") + @Test + public void testComplexParamSpecifyParamType() throws RemotingException { + mockInvoker = mock(Invoker.class); + given(mockInvoker.getInterface()).willReturn(DemoService.class); + given(mockInvoker.getUrl()).willReturn(URL.valueOf("dubbo://127.0.0.1:20886/demo")); + given(mockInvoker.invoke(any(Invocation.class))).willReturn(new RpcResult("ok")); + 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")); + + DubboProtocol.getDubboProtocol().export(mockInvoker); + + // 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("Use default service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService.\r\n\"ok\"\r\n")); + + // 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("Use default service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService.\r\n\"ok\"\r\n")); + } + + @SuppressWarnings("unchecked") + @Test + public void testComplexParamSpecifyWrongParamType() throws RemotingException { + mockInvoker = mock(Invoker.class); + given(mockInvoker.getInterface()).willReturn(DemoService.class); + given(mockInvoker.getUrl()).willReturn(URL.valueOf("dubbo://127.0.0.1:20886/demo")); + given(mockInvoker.invoke(any(Invocation.class))).willReturn(new RpcResult("ok")); + 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")); + + DubboProtocol.getDubboProtocol().export(mockInvoker); + + // 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); + + // 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); + } + + @SuppressWarnings("unchecked") @Test public void testInvokeAutoFindMethod() throws RemotingException {