Skip to content

Commit 0e0fbd5

Browse files
LiZhenNetbeiwei30
authored andcommitted
Refactor telnet invoke command (#3210)
* refactor telnet invoke command * add select command for telnet * fix test case
1 parent f76ae21 commit 0e0fbd5

File tree

5 files changed

+454
-252
lines changed

5 files changed

+454
-252
lines changed

dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java

Lines changed: 139 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
package org.apache.dubbo.rpc.protocol.dubbo.telnet;
1818

1919
import com.alibaba.fastjson.JSON;
20-
import com.alibaba.fastjson.JSONObject;
2120
import org.apache.dubbo.common.extension.Activate;
21+
import org.apache.dubbo.common.utils.CollectionUtils;
2222
import org.apache.dubbo.common.utils.ReflectUtils;
2323
import org.apache.dubbo.common.utils.StringUtils;
2424
import org.apache.dubbo.remoting.Channel;
@@ -42,90 +42,13 @@
4242
* InvokeTelnetHandler
4343
*/
4444
@Activate
45-
@Help(parameter = "[service.]method(args) [-p parameter classes]", summary = "Invoke the service method.",
45+
@Help(parameter = "[service.]method(args) ", summary = "Invoke the service method.",
4646
detail = "Invoke the service method.")
4747
public class InvokeTelnetHandler implements TelnetHandler {
48-
private static Method findMethod(List<ProviderMethodModel> methods, String method, List<Object> args,
49-
Class<?>[] paramTypes) {
50-
for (ProviderMethodModel model : methods) {
51-
Method m = model.getMethod();
52-
if (isMatch(m, args, paramTypes, method)) {
53-
return m;
54-
}
55-
}
56-
return null;
57-
}
58-
59-
private static boolean isMatch(Method method, List<Object> args, Class<?>[] paramClasses, String lookupMethodName) {
60-
if (!method.getName().equals(lookupMethodName)) {
61-
return false;
62-
}
63-
64-
Class<?> types[] = method.getParameterTypes();
65-
if (types.length != args.size()) {
66-
return false;
67-
}
68-
for (int i = 0; i < types.length; i++) {
69-
Class<?> type = types[i];
70-
Object arg = args.get(i);
71-
72-
if (paramClasses != null && type != paramClasses[i]) {
73-
return false;
74-
}
7548

76-
if (arg == null) {
77-
// if the type is primitive, the method to invoke will cause NullPointerException definitely
78-
// so we can offer a specified error message to the invoker in advance and avoid unnecessary invoking
79-
if (type.isPrimitive()) {
80-
throw new NullPointerException(String.format("The type of No.%d parameter is primitive(%s), " +
81-
"but the value passed is null.", i + 1, type.getName()));
82-
}
83-
84-
// if the type is not primitive, we choose to believe what the invoker want is a null value
85-
continue;
86-
}
87-
88-
if (ReflectUtils.isPrimitive(arg.getClass())) {
89-
// allow string arg to enum type, @see PojoUtils.realize0()
90-
if (arg instanceof String && type.isEnum()) {
91-
continue;
92-
}
93-
94-
if (!ReflectUtils.isPrimitive(type)) {
95-
return false;
96-
}
97-
98-
if (!ReflectUtils.isCompatible(type, arg)) {
99-
return false;
100-
}
101-
} else if (arg instanceof Map) {
102-
String name = (String) ((Map<?, ?>) arg).get("class");
103-
if (StringUtils.isNotEmpty(name)) {
104-
Class<?> cls = ReflectUtils.forName(name);
105-
if (!type.isAssignableFrom(cls)) {
106-
return false;
107-
}
108-
} else {
109-
if (arg instanceof JSONObject) {
110-
try {
111-
((JSONObject) arg).toJavaObject(type);
112-
} catch (Exception ex) {
113-
return false;
114-
}
115-
}
116-
}
117-
} else if (arg instanceof Collection) {
118-
if (!type.isArray() && !type.isAssignableFrom(arg.getClass())) {
119-
return false;
120-
}
121-
} else {
122-
if (!type.isAssignableFrom(arg.getClass())) {
123-
return false;
124-
}
125-
}
126-
}
127-
return true;
128-
}
49+
public static final String INVOKE_MESSAGE_KEY = "telnet.invoke.method.message";
50+
public static final String INVOKE_METHOD_LIST_KEY = "telnet.invoke.method.list";
51+
public static final String INVOKE_METHOD_PROVIDER_KEY = "telnet.invoke.method.provider";
12952

13053
@Override
13154
@SuppressWarnings("unchecked")
@@ -136,33 +59,9 @@ public String telnet(Channel channel, String message) {
13659
"invoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})";
13760
}
13861

139-
StringBuilder buf = new StringBuilder();
14062
String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
141-
if (!StringUtils.isEmpty(service)) {
142-
buf.append("Use default service ").append(service).append(".");
143-
}
14463

14564
int i = message.indexOf("(");
146-
String originalMessage = message;
147-
Class<?>[] paramTypes = null;
148-
if (message.contains("-p")) {
149-
message = originalMessage.substring(0, originalMessage.indexOf("-p")).trim();
150-
String paramClassesString = originalMessage.substring(originalMessage.indexOf("-p") + 2).trim();
151-
if (paramClassesString.length() > 0) {
152-
String[] split = paramClassesString.split("\\s+");
153-
if (split.length > 0) {
154-
paramTypes = new Class[split.length];
155-
for (int j = 0; j < split.length; j++) {
156-
try {
157-
paramTypes[j] = Class.forName(split[j]);
158-
} catch (ClassNotFoundException e) {
159-
return "Unknown parameter class for name " + split[j];
160-
}
161-
}
162-
163-
}
164-
}
165-
}
16665

16766
if (i < 0 || !message.endsWith(")")) {
16867
return "Invalid parameters, format: service.method(args)";
@@ -182,32 +81,44 @@ public String telnet(Channel channel, String message) {
18281
} catch (Throwable t) {
18382
return "Invalid json argument, cause: " + t.getMessage();
18483
}
185-
if (paramTypes != null) {
186-
if (paramTypes.length != list.size()) {
187-
return "Parameter's number does not match the number of parameter class";
188-
}
189-
List<Object> listOfActualClass = new ArrayList<>(list.size());
190-
for (int ii = 0; ii < list.size(); ii++) {
191-
if (list.get(ii) instanceof JSONObject) {
192-
JSONObject jsonObject = (JSONObject) list.get(ii);
193-
listOfActualClass.add(jsonObject.toJavaObject(paramTypes[ii]));
194-
} else {
195-
listOfActualClass.add(list.get(ii));
196-
}
197-
}
198-
list = listOfActualClass;
199-
}
200-
84+
StringBuilder buf = new StringBuilder();
20185
Method invokeMethod = null;
20286
ProviderModel selectedProvider = null;
203-
for (ProviderModel provider : ApplicationModel.allProviderModels()) {
204-
if (isServiceMatch(service, provider)) {
205-
invokeMethod = findMethod(provider.getAllMethods(), method, list, paramTypes);
206-
selectedProvider = provider;
207-
break;
87+
if (isInvokedSelectCommand(channel)) {
88+
selectedProvider = (ProviderModel) channel.getAttribute(INVOKE_METHOD_PROVIDER_KEY);
89+
invokeMethod = (Method) channel.getAttribute(SelectTelnetHandler.SELECT_METHOD_KEY);
90+
} else {
91+
for (ProviderModel provider : ApplicationModel.allProviderModels()) {
92+
if (isServiceMatch(service, provider)) {
93+
selectedProvider = provider;
94+
List<Method> methodList = findSameSignatureMethod(provider.getAllMethods(), method, list);
95+
if (CollectionUtils.isNotEmpty(methodList)) {
96+
if (methodList.size() == 1) {
97+
invokeMethod = methodList.get(0);
98+
} else {
99+
List<Method> matchMethods = findMatchMethods(methodList, list);
100+
if (CollectionUtils.isNotEmpty(matchMethods)) {
101+
if (matchMethods.size() == 1) {
102+
invokeMethod = matchMethods.get(0);
103+
} else { //exist overridden method
104+
channel.setAttribute(INVOKE_METHOD_PROVIDER_KEY, provider);
105+
channel.setAttribute(INVOKE_METHOD_LIST_KEY, matchMethods);
106+
channel.setAttribute(INVOKE_MESSAGE_KEY, message);
107+
printSelectMessage(buf, matchMethods);
108+
return buf.toString();
109+
}
110+
}
111+
}
112+
}
113+
break;
114+
}
208115
}
209116
}
210117

118+
119+
if (!StringUtils.isEmpty(service)) {
120+
buf.append("Use default service ").append(service).append(".");
121+
}
211122
if (selectedProvider != null) {
212123
if (invokeMethod != null) {
213124
try {
@@ -240,10 +151,111 @@ public String telnet(Channel channel, String message) {
240151
return buf.toString();
241152
}
242153

154+
243155
private boolean isServiceMatch(String service, ProviderModel provider) {
244156
return provider.getServiceName().equalsIgnoreCase(service)
245157
|| provider.getServiceInterfaceClass().getSimpleName().equalsIgnoreCase(service)
246158
|| provider.getServiceInterfaceClass().getName().equalsIgnoreCase(service)
247159
|| StringUtils.isEmpty(service);
248160
}
161+
162+
private List<Method> findSameSignatureMethod(List<ProviderMethodModel> methods, String lookupMethodName, List<Object> args) {
163+
List<Method> sameSignatureMethods = new ArrayList<>();
164+
for (ProviderMethodModel model : methods) {
165+
Method method = model.getMethod();
166+
if (method.getName().equals(lookupMethodName) && method.getParameterTypes().length == args.size()) {
167+
sameSignatureMethods.add(method);
168+
}
169+
}
170+
return sameSignatureMethods;
171+
}
172+
173+
private List<Method> findMatchMethods(List<Method> methods, List<Object> args) {
174+
List<Method> matchMethod = new ArrayList<>();
175+
for (Method method : methods) {
176+
if (isMatch(method, args)) {
177+
matchMethod.add(method);
178+
}
179+
}
180+
return matchMethod;
181+
}
182+
183+
private static boolean isMatch(Method method, List<Object> args) {
184+
Class<?>[] types = method.getParameterTypes();
185+
if (types.length != args.size()) {
186+
return false;
187+
}
188+
for (int i = 0; i < types.length; i++) {
189+
Class<?> type = types[i];
190+
Object arg = args.get(i);
191+
192+
if (arg == null) {
193+
if (type.isPrimitive()) {
194+
return false;
195+
}
196+
197+
// if the type is not primitive, we choose to believe what the invoker want is a null value
198+
continue;
199+
}
200+
201+
if (ReflectUtils.isPrimitive(arg.getClass())) {
202+
// allow string arg to enum type, @see PojoUtils.realize0()
203+
if (arg instanceof String && type.isEnum()) {
204+
continue;
205+
}
206+
207+
if (!ReflectUtils.isPrimitive(type)) {
208+
return false;
209+
}
210+
211+
if (!ReflectUtils.isCompatible(type, arg)) {
212+
return false;
213+
}
214+
} else if (arg instanceof Map) {
215+
String name = (String) ((Map<?, ?>) arg).get("class");
216+
if (StringUtils.isNotEmpty(name)) {
217+
Class<?> cls = ReflectUtils.forName(name);
218+
if (!type.isAssignableFrom(cls)) {
219+
return false;
220+
}
221+
} else {
222+
return true;
223+
}
224+
} else if (arg instanceof Collection) {
225+
if (!type.isArray() && !type.isAssignableFrom(arg.getClass())) {
226+
return false;
227+
}
228+
} else {
229+
if (!type.isAssignableFrom(arg.getClass())) {
230+
return false;
231+
}
232+
}
233+
}
234+
return true;
235+
}
236+
237+
private void printSelectMessage(StringBuilder buf, List<Method> methods) {
238+
buf.append("Methods:\r\n");
239+
for (int i = 0; i < methods.size(); i++) {
240+
Method method = methods.get(i);
241+
buf.append((i + 1) + ". " + method.getName() + "(");
242+
Class<?>[] parameterTypes = method.getParameterTypes();
243+
for (int n = 0; n < parameterTypes.length; n++) {
244+
buf.append(parameterTypes[n].getSimpleName());
245+
if (n != parameterTypes.length - 1) {
246+
buf.append(",");
247+
}
248+
}
249+
buf.append(")\r\n");
250+
}
251+
buf.append("Please use the select command to select the method you want to invoke. eg: select 1");
252+
}
253+
254+
private boolean isInvokedSelectCommand(Channel channel) {
255+
if (channel.hasAttribute(SelectTelnetHandler.SELECT_KEY)) {
256+
channel.removeAttribute(SelectTelnetHandler.SELECT_KEY);
257+
return true;
258+
}
259+
return false;
260+
}
249261
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.dubbo.rpc.protocol.dubbo.telnet;
18+
19+
import org.apache.dubbo.common.extension.Activate;
20+
import org.apache.dubbo.common.utils.CollectionUtils;
21+
import org.apache.dubbo.common.utils.StringUtils;
22+
import org.apache.dubbo.remoting.Channel;
23+
import org.apache.dubbo.remoting.telnet.TelnetHandler;
24+
import org.apache.dubbo.remoting.telnet.support.Help;
25+
26+
import java.lang.reflect.Method;
27+
import java.util.List;
28+
29+
/**
30+
* SelectTelnetHandler
31+
*/
32+
@Activate
33+
@Help(parameter = "[index]", summary = "Select the index of the method you want to invoke.",
34+
detail = "Select the index of the method you want to invoke.")
35+
public class SelectTelnetHandler implements TelnetHandler {
36+
public static final String SELECT_METHOD_KEY = "telnet.select.method";
37+
public static final String SELECT_KEY = "telnet.select";
38+
39+
private InvokeTelnetHandler invokeTelnetHandler = new InvokeTelnetHandler();
40+
41+
@Override
42+
@SuppressWarnings("unchecked")
43+
public String telnet(Channel channel, String message) {
44+
if (message == null || message.length() == 0) {
45+
return "Please input the index of the method you want to invoke, eg: \r\n select 1";
46+
}
47+
List<Method> methodList = (List<Method>) channel.getAttribute(InvokeTelnetHandler.INVOKE_METHOD_LIST_KEY);
48+
if (CollectionUtils.isEmpty(methodList)) {
49+
return "Please use the invoke command first.";
50+
}
51+
if (!StringUtils.isInteger(message) || Integer.parseInt(message) < 1 || Integer.parseInt(message) > methodList.size()) {
52+
return "Illegal index ,please input select 1~" + methodList.size();
53+
}
54+
Method method = methodList.get(Integer.parseInt(message));
55+
channel.setAttribute(SELECT_METHOD_KEY, method);
56+
channel.setAttribute(SELECT_KEY, Boolean.TRUE);
57+
String invokeMessage = (String) channel.getAttribute(InvokeTelnetHandler.INVOKE_MESSAGE_KEY);
58+
return invokeTelnetHandler.telnet(channel, invokeMessage);
59+
}
60+
}

dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ cd=org.apache.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler
44
pwd=org.apache.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler
55
invoke=org.apache.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler
66
trace=org.apache.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler
7-
count=org.apache.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler
7+
count=org.apache.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler
8+
select=org.apache.dubbo.rpc.protocol.dubbo.telnet.SelectTelnetHandler

0 commit comments

Comments
 (0)