Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dubbo-2766]Fix 2766 and enhance the invoke command #2801

Merged
merged 13 commits into from
Dec 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ public String telnet(Channel channel, String message) {
Help help = handler.getClass().getAnnotation(Help.class);
List<String> row = new ArrayList<String>();
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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -40,28 +42,32 @@
* 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<Object> args) {
private static Method findMethod(Exporter<?> exporter, String method, List<Object> 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<Object> args) {
private static boolean isMatch(Class<?>[] types, List<Object> args, Class<?>[] paramClasses) {
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
Expand All @@ -83,8 +89,8 @@ private static boolean isMatch(Class<?>[] types, List<Object> 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) {
Expand Down Expand Up @@ -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)";
}
Expand All @@ -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<Object> 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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -179,5 +220,4 @@ public String telnet(Channel channel, String message) {
}
return buf.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,8 @@ public interface DemoService {

long add(int a, long b);

int getPerson(Person person);

int getPerson(Person person1, Person perso2);

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down