1717package org .apache .dubbo .rpc .protocol .dubbo .telnet ;
1818
1919import com .alibaba .fastjson .JSON ;
20- import com .alibaba .fastjson .JSONObject ;
2120import org .apache .dubbo .common .extension .Activate ;
21+ import org .apache .dubbo .common .utils .CollectionUtils ;
2222import org .apache .dubbo .common .utils .ReflectUtils ;
2323import org .apache .dubbo .common .utils .StringUtils ;
2424import org .apache .dubbo .remoting .Channel ;
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." )
4747public 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}
0 commit comments