You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2021/07/19 06:51:24 UTC

[dubbo] branch 3.0 updated: Transfer qos interface to BaseCommand (#8175)

This is an automated email from the ASF dual-hosted git repository.

liujun pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new a2711f9  Transfer qos interface to BaseCommand (#8175)
a2711f9 is described below

commit a2711f9e2e2becc91ffc2b375c3b749dc0d24a38
Author: Honghan Zhu <ne...@163.com>
AuthorDate: Mon Jul 19 14:50:42 2021 +0800

    Transfer qos interface to BaseCommand (#8175)
---
 .../apache/dubbo/config/RegistryConfigTest.java    |  19 +-
 .../dubbo/qos/command/impl/ChangeTelnet.java       |  66 ++++
 .../impl/CountTelnet.java}                         | 104 +++--
 .../impl/InvokeTelnet.java}                        |  68 ++--
 .../impl/PortTelnet.java}                          |  28 +-
 .../impl/PwdTelnet.java}                           |  29 +-
 .../impl/SelectTelnet.java}                        |  46 +--
 .../impl/ShutdownTelnet.java}                      |  29 +-
 .../apache/dubbo/qos/legacy/ListTelnetHandler.java | 166 --------
 .../org.apache.dubbo.qos.command.BaseCommand       |   7 +
 .../org.apache.dubbo.remoting.telnet.TelnetHandler |   8 -
 .../dubbo/qos/command/impl/ChangeTelnetTest.java   | 117 ++++++
 .../impl/CountTelnetTest.java}                     |  33 +-
 .../dubbo/qos/command/impl/InvokeTelnetTest.java   | 257 ++++++++++++
 .../impl/PortTelnetTest.java}                      |  45 ++-
 .../dubbo/qos/command/impl/PwdTelnetTest.java      |  77 ++++
 .../impl/SelectTelnetTest.java}                    |  86 ++--
 .../impl/ShutdownTelnetTest.java}                  |  44 ++-
 .../qos/command/impl/channel/MockNettyChannel.java | 439 +++++++++++++++++++++
 .../dubbo/qos/command/util/CommandHelperTest.java  |  15 +
 .../dubbo/qos/legacy/CurrentTelnetHandlerTest.java |  63 ---
 .../dubbo/qos/legacy/InvokerTelnetHandlerTest.java | 300 --------------
 .../dubbo/qos/legacy/ListTelnetHandlerTest.java    | 150 -------
 dubbo-remoting/dubbo-remoting-zookeeper/pom.xml    |   6 +
 24 files changed, 1269 insertions(+), 933 deletions(-)

diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java
index beba43e..76e79bd 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java
@@ -211,7 +211,7 @@ public class RegistryConfigTest {
     public void testMetaData() {
         RegistryConfig config = new RegistryConfig();
         Map<String, String> metaData = config.getMetaData();
-        Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: "+metaData);
+        Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData);
     }
 
     @Test
@@ -223,18 +223,15 @@ public class RegistryConfigTest {
         SysProps.setProperty("zookeeper.address", "localhost");
         SysProps.setProperty("zookeeper.port", "2188");
 
-        try {
 
-            DubboBootstrap.getInstance()
-                    .application("demo-app")
-                    .initialize();
+        DubboBootstrap.getInstance()
+            .application("demo-app")
+            .initialize();
+        Collection<RegistryConfig> registries = ApplicationModel.getConfigManager().getRegistries();
+        Assertions.assertEquals(1, registries.size());
+        RegistryConfig registryConfig = registries.iterator().next();
+        Assertions.assertEquals("zookeeper://localhost:2188", registryConfig.getAddress());
 
-            Collection<RegistryConfig> registries = ApplicationModel.getConfigManager().getRegistries();
-            Assertions.assertEquals(1, registries.size());
-            RegistryConfig registryConfig = registries.iterator().next();
-            Assertions.assertEquals("zookeeper://localhost:2188", registryConfig.getAddress());
-        } finally {
-        }
     }
 
     public void testPreferredWithTrueValue() {
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java
new file mode 100644
index 0000000..d6424ec
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java
@@ -0,0 +1,66 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+import io.netty.channel.Channel;
+import io.netty.util.AttributeKey;
+
+@Cmd(name = "cd", summary = "Change default service.", example = {
+    "cd [service]"
+})
+public class ChangeTelnet implements BaseCommand {
+
+    public static final AttributeKey<String> SERVICE_KEY = AttributeKey.valueOf("telnet.service");
+
+    @Override
+    public String execute(CommandContext commandContext, String[] args) {
+        Channel channel = commandContext.getRemote();
+
+        if (args == null || args.length < 1) {
+            return "Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService";
+        }
+        String message = args[0];
+        StringBuilder buf = new StringBuilder();
+        if ("/".equals(message) || "..".equals(message)) {
+            String service = channel.attr(SERVICE_KEY).getAndRemove();
+            buf.append("Cancelled default service ").append(service).append(".");
+        } else {
+            boolean found = false;
+            for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+                if (message.equals(exporter.getInvoker().getInterface().getSimpleName())
+                    || message.equals(exporter.getInvoker().getInterface().getName())
+                    || message.equals(exporter.getInvoker().getUrl().getPath())) {
+                    found = true;
+                    break;
+                }
+            }
+            if (found) {
+                channel.attr(SERVICE_KEY).set(message);
+                buf.append("Used the ").append(message).append(" as default.\r\nYou can cancel default service by command: cd /");
+            } else {
+                buf.append("No such service ").append(message);
+            }
+        }
+        return buf.toString();
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/CountTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
similarity index 60%
rename from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/CountTelnetHandler.java
rename to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
index cab2799..5bd8ecc 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/CountTelnetHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
@@ -14,57 +14,60 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
 import org.apache.dubbo.remoting.RemotingException;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
-import org.apache.dubbo.remoting.telnet.support.Help;
 import org.apache.dubbo.remoting.telnet.support.TelnetUtils;
+import org.apache.dubbo.remoting.utils.PayloadDropper;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.RpcStatus;
 import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
 
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+
 import java.lang.reflect.Method;
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.List;
 
-/**
- * CountTelnetHandler
- */
-@Activate
-@Help(parameter = "[service] [method] [times]", summary = "Count the service.", detail = "Count the service.")
-public class CountTelnetHandler implements TelnetHandler {
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
 
+@Cmd(name = "count", summary = "Count the service.", example = {
+    "count [service] [method] [times]"
+})
+public class CountTelnet implements BaseCommand {
     @Override
-    public String telnet(final Channel channel, String message) {
-        String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+    public String execute(CommandContext commandContext, String[] args) {
+        Channel channel = commandContext.getRemote();
+        String service = channel.attr(ChangeTelnet.SERVICE_KEY).get();
         if ((service == null || service.length() == 0)
-                && (message == null || message.length() == 0)) {
+            && (args == null || args.length == 0)) {
             return "Please input service name, eg: \r\ncount XxxService\r\ncount XxxService xxxMethod\r\ncount XxxService xxxMethod 10\r\nor \"cd XxxService\" firstly.";
         }
         StringBuilder buf = new StringBuilder();
         if (service != null && service.length() > 0) {
             buf.append("Use default service ").append(service).append(".\r\n");
         }
-        String[] parts = message.split("\\s+");
         String method;
         String times;
         if (service == null || service.length() == 0) {
-            service = parts.length > 0 ? parts[0] : null;
-            method = parts.length > 1 ? parts[1] : null;
+            service = args[0];
+            method = args.length > 1 ? args[1] : null;
         } else {
-            method = parts.length > 0 ? parts[0] : null;
+            method = args.length > 0 ? args[0] : null;
         }
         if (StringUtils.isInteger(method)) {
             times = method;
             method = null;
         } else {
-            times = parts.length > 2 ? parts[2] : "1";
+            times = args.length > 2 ? args[2] : "1";
         }
         if (!StringUtils.isInteger(times)) {
             return "Illegal times " + times + ", must be integer.";
@@ -73,8 +76,8 @@ public class CountTelnetHandler implements TelnetHandler {
         Invoker<?> invoker = null;
         for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
             if (service.equals(exporter.getInvoker().getInterface().getSimpleName())
-                    || service.equals(exporter.getInvoker().getInterface().getName())
-                    || service.equals(exporter.getInvoker().getUrl().getPath())) {
+                || service.equals(exporter.getInvoker().getInterface().getName())
+                || service.equals(exporter.getInvoker().getUrl().getPath())) {
                 invoker = exporter.getInvoker();
                 break;
             }
@@ -83,29 +86,25 @@ public class CountTelnetHandler implements TelnetHandler {
             if (t > 0) {
                 final String mtd = method;
                 final Invoker<?> inv = invoker;
-                final String prompt = channel.getUrl().getParameter("prompt", "telnet");
-                Thread thread = new Thread(new Runnable() {
-                    @Override
-                    public void run() {
-                        for (int i = 0; i < t; i++) {
-                            String result = count(inv, mtd);
-                            try {
-                                channel.send("\r\n" + result);
-                            } catch (RemotingException e1) {
-                                return;
-                            }
-                            if (i < t - 1) {
-                                try {
-                                    Thread.sleep(1000);
-                                } catch (InterruptedException e) {
-                                }
-                            }
-                        }
+                final String prompt = "telnet";
+                Thread thread = new Thread(() -> {
+                    for (int i = 0; i < t; i++) {
+                        String result = count(inv, mtd);
                         try {
-                            channel.send("\r\n" + prompt + "> ");
+                            send(channel, "\r\n" + result);
                         } catch (RemotingException e1) {
                             return;
                         }
+                        if (i < t - 1) {
+                            try {
+                                Thread.sleep(1000);
+                            } catch (InterruptedException ignored) {
+                            }
+                        }
+                    }
+                    try {
+                        send(channel, "\r\n" + prompt + "> ");
+                    } catch (RemotingException ignored) {
                     }
                 }, "TelnetCount");
                 thread.setDaemon(true);
@@ -117,6 +116,25 @@ public class CountTelnetHandler implements TelnetHandler {
         return buf.toString();
     }
 
+    public void send(Channel channel, Object message) throws RemotingException {
+        boolean success;
+        int timeout = 0;
+        try {
+            ChannelFuture future = channel.writeAndFlush(message);
+            success = future.await(DEFAULT_TIMEOUT);
+            Throwable cause = future.cause();
+            if (cause != null) {
+                throw cause;
+            }
+        } catch (Throwable e) {
+            throw new RemotingException((InetSocketAddress) channel.localAddress(), (InetSocketAddress) channel.remoteAddress(), "Failed to send message " + PayloadDropper.getRequestWithoutData(message) + " to " + channel.remoteAddress().toString() + ", cause: " + e.getMessage(), e);
+        }
+        if (!success) {
+            throw new RemotingException((InetSocketAddress) channel.localAddress(), (InetSocketAddress) channel.remoteAddress(), "Failed to send message " + PayloadDropper.getRequestWithoutData(message) + " to " + channel.remoteAddress().toString()
+                + "in timeout(" + timeout + "ms) limit");
+        }
+    }
+
     private String count(Invoker<?> invoker, String method) {
         URL url = invoker.getUrl();
         List<List<String>> table = new ArrayList<List<String>>();
@@ -130,7 +148,7 @@ public class CountTelnetHandler implements TelnetHandler {
         if (method == null || method.length() == 0) {
             for (Method m : invoker.getInterface().getMethods()) {
                 RpcStatus count = RpcStatus.getStatus(url, m.getName());
-                table.add(createRow(m.getName(),count));
+                table.add(createRow(m.getName(), count));
             }
         } else {
             boolean found = false;
@@ -142,7 +160,7 @@ public class CountTelnetHandler implements TelnetHandler {
             }
             if (found) {
                 RpcStatus count = RpcStatus.getStatus(url, method);
-                table.add(createRow(method,count));
+                table.add(createRow(method, count));
             } else {
                 return "No such method " + method + " in class " + invoker.getInterface().getName();
             }
@@ -150,7 +168,7 @@ public class CountTelnetHandler implements TelnetHandler {
         return TelnetUtils.toTable(header, table);
     }
 
-    private List<String> createRow(String methodName,RpcStatus count) {
+    private List<String> createRow(String methodName, RpcStatus count) {
         List<String> row = new ArrayList<String>();
         row.add(methodName);
         row.add(String.valueOf(count.getTotal()));
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/InvokeTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java
similarity index 79%
rename from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/InvokeTelnetHandler.java
rename to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java
index c63aacf..7790845 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/InvokeTelnetHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java
@@ -14,21 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
-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;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
-import org.apache.dubbo.remoting.telnet.support.Help;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
 import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.MethodDescriptor;
 import org.apache.dubbo.rpc.model.ProviderModel;
 
 import com.alibaba.fastjson.JSON;
+import io.netty.channel.Channel;
+import io.netty.util.AttributeKey;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -39,29 +40,26 @@ import java.util.Set;
 
 import static org.apache.dubbo.common.utils.PojoUtils.realize;
 
-/**
- * InvokeTelnetHandler
- */
-@Activate
-@Help(parameter = "[service.]method(args) ", summary = "Invoke the service method.",
-        detail = "Invoke the service method.")
-public class InvokeTelnetHandler implements TelnetHandler {
+@Cmd(name = "invoke", summary = "Invoke the service method.", example = {
+    "invoke IHelloService.sayHello(\"xxxx\")"
+})
+public class InvokeTelnet implements BaseCommand {
+    public static final AttributeKey<String> INVOKE_MESSAGE_KEY = AttributeKey.valueOf("telnet.invoke.method.message");
+    public static final AttributeKey<List<Method>> INVOKE_METHOD_LIST_KEY = AttributeKey.valueOf("telnet.invoke.method.list");
+    public static final AttributeKey<ProviderModel> INVOKE_METHOD_PROVIDER_KEY = AttributeKey.valueOf("telnet.invoke.method.provider");
 
-    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")
-    public String telnet(Channel channel, String message) {
-        if (StringUtils.isEmpty(message)) {
+    public String execute(CommandContext commandContext, String[] args) {
+        if (args == null || args.length == 0) {
             return "Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\n" +
-                    "invoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\n" +
-                    "invoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})";
+                "invoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\n" +
+                "invoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})";
         }
+        Channel channel = commandContext.getRemote();
+        String service = channel.attr(ChangeTelnet.SERVICE_KEY).get();
 
-        String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
-
+        String message = args[0];
         int i = message.indexOf("(");
 
         if (i < 0 || !message.endsWith(")")) {
@@ -69,7 +67,7 @@ public class InvokeTelnetHandler implements TelnetHandler {
         }
 
         String method = message.substring(0, i).trim();
-        String args = message.substring(i + 1, message.length() - 1).trim();
+        String param = message.substring(i + 1, message.length() - 1).trim();
         i = method.lastIndexOf(".");
         if (i >= 0) {
             service = method.substring(0, i).trim();
@@ -78,7 +76,7 @@ public class InvokeTelnetHandler implements TelnetHandler {
 
         List<Object> list;
         try {
-            list = JSON.parseArray("[" + args + "]", Object.class);
+            list = JSON.parseArray("[" + param + "]", Object.class);
         } catch (Throwable t) {
             return "Invalid json argument, cause: " + t.getMessage();
         }
@@ -86,8 +84,8 @@ public class InvokeTelnetHandler implements TelnetHandler {
         Method invokeMethod = null;
         ProviderModel selectedProvider = null;
         if (isInvokedSelectCommand(channel)) {
-            selectedProvider = (ProviderModel) channel.getAttribute(INVOKE_METHOD_PROVIDER_KEY);
-            invokeMethod = (Method) channel.getAttribute(SelectTelnetHandler.SELECT_METHOD_KEY);
+            selectedProvider = channel.attr(INVOKE_METHOD_PROVIDER_KEY).get();
+            invokeMethod = channel.attr(SelectTelnet.SELECT_METHOD_KEY).get();
         } else {
             for (ProviderModel provider : ApplicationModel.allProviderModels()) {
                 if (!isServiceMatch(service, provider)) {
@@ -110,9 +108,9 @@ public class InvokeTelnetHandler implements TelnetHandler {
                     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);
+                        channel.attr(INVOKE_METHOD_PROVIDER_KEY).set(provider);
+                        channel.attr(INVOKE_METHOD_LIST_KEY).set(matchMethods);
+                        channel.attr(INVOKE_MESSAGE_KEY).set(message);
                         printSelectMessage(buf, matchMethods);
                         return buf.toString();
                     }
@@ -135,7 +133,7 @@ public class InvokeTelnetHandler implements TelnetHandler {
         }
         try {
             Object[] array = realize(list.toArray(), invokeMethod.getParameterTypes(),
-                    invokeMethod.getGenericParameterTypes());
+                invokeMethod.getGenericParameterTypes());
             long start = System.currentTimeMillis();
             AppResponse result = new AppResponse();
             try {
@@ -159,9 +157,9 @@ public class InvokeTelnetHandler implements TelnetHandler {
 
     private boolean isServiceMatch(String service, ProviderModel provider) {
         return provider.getServiceKey().equalsIgnoreCase(service)
-                || provider.getServiceInterfaceClass().getSimpleName().equalsIgnoreCase(service)
-                || provider.getServiceInterfaceClass().getName().equalsIgnoreCase(service)
-                || StringUtils.isEmpty(service);
+            || provider.getServiceInterfaceClass().getSimpleName().equalsIgnoreCase(service)
+            || provider.getServiceInterfaceClass().getName().equalsIgnoreCase(service)
+            || StringUtils.isEmpty(service);
     }
 
     private List<Method> findSameSignatureMethod(Set<MethodDescriptor> methods, String lookupMethodName, List<Object> args) {
@@ -257,8 +255,8 @@ public class InvokeTelnetHandler implements TelnetHandler {
     }
 
     private boolean isInvokedSelectCommand(Channel channel) {
-        if (channel.hasAttribute(SelectTelnetHandler.SELECT_KEY)) {
-            channel.removeAttribute(SelectTelnetHandler.SELECT_KEY);
+        if (channel.attr(SelectTelnet.SELECT_KEY).get() != null) {
+            channel.attr(SelectTelnet.SELECT_KEY).remove();
             return true;
         }
         return false;
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/PortTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java
similarity index 82%
rename from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/PortTelnetHandler.java
rename to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java
index 75a94e6..d6ce35e 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/PortTelnetHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java
@@ -14,35 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
-import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
 import org.apache.dubbo.remoting.exchange.ExchangeChannel;
 import org.apache.dubbo.remoting.exchange.ExchangeServer;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
-import org.apache.dubbo.remoting.telnet.support.Help;
 import org.apache.dubbo.rpc.ProtocolServer;
 import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
 
 import java.util.Collection;
 
-/**
- * ServerTelnetHandler
- */
-@Activate
-@Help(parameter = "[-l] [port]", summary = "Print server ports and connections.", detail = "Print server ports and connections.")
-public class PortTelnetHandler implements TelnetHandler {
-
+@Cmd(name = "ps", summary = "Print server ports and connections.", example = {
+    "ps -l [port]", "ps", "ps -l", "ps -l 20880"
+})
+public class PortTelnet implements BaseCommand {
     @Override
-    public String telnet(Channel channel, String message) {
+    public String execute(CommandContext commandContext, String[] args) {
         StringBuilder buf = new StringBuilder();
         String port = null;
         boolean detail = false;
-        if (message.length() > 0) {
-            String[] parts = message.split("\\s+");
-            for (String part : parts) {
+        if (args.length > 0) {
+            for (String part : args) {
                 if ("-l".equals(part)) {
                     detail = true;
                 } else {
@@ -92,5 +87,4 @@ public class PortTelnetHandler implements TelnetHandler {
         }
         return buf.toString();
     }
-
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/CurrentTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PwdTelnet.java
similarity index 59%
rename from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/CurrentTelnetHandler.java
rename to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PwdTelnet.java
index 395c7e5..366077b 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/CurrentTelnetHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PwdTelnet.java
@@ -14,26 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.remoting.Channel;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
-import org.apache.dubbo.remoting.telnet.support.Help;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
 
-/**
- * CurrentServiceTelnetHandler
- */
-@Activate
-@Help(parameter = "", summary = "Print working default service.", detail = "Print working default service.")
-public class CurrentTelnetHandler implements TelnetHandler {
+import java.util.Arrays;
 
+@Cmd(name = "pwd", summary = "Print working default service.", example = {
+    "pwd"
+})
+public class PwdTelnet implements BaseCommand {
     @Override
-    public String telnet(Channel channel, String message) {
-        if (message.length() > 0) {
-            return "Unsupported parameter " + message + " for pwd.";
+    public String execute(CommandContext commandContext, String[] args) {
+        if (args.length > 0) {
+            return "Unsupported parameter " + Arrays.toString(args) + " for pwd.";
         }
-        String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+        String service = commandContext.getRemote().attr(ChangeTelnet.SERVICE_KEY).get();
         StringBuilder buf = new StringBuilder();
         if (service == null || service.length() == 0) {
             buf.append('/');
@@ -42,5 +40,4 @@ public class CurrentTelnetHandler implements TelnetHandler {
         }
         return buf.toString();
     }
-
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/SelectTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
similarity index 53%
rename from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/SelectTelnetHandler.java
rename to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
index 2db7f54..b15cecc 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/SelectTelnetHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
@@ -14,37 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
-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 org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+
+import io.netty.channel.Channel;
+import io.netty.util.AttributeKey;
 
 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";
+@Cmd(name = "select", summary = "Select the index of the method you want to invoke", example = {
+    "select [index]"
+})
+public class SelectTelnet implements BaseCommand {
+    public static final AttributeKey<Boolean> SELECT_KEY = AttributeKey.valueOf("telnet.select");
+    public static final AttributeKey<Method> SELECT_METHOD_KEY = AttributeKey.valueOf("telnet.select.method");
 
-    private InvokeTelnetHandler invokeTelnetHandler = new InvokeTelnetHandler();
+    private final InvokeTelnet invokeTelnet = new InvokeTelnet();
 
     @Override
-    @SuppressWarnings("unchecked")
-    public String telnet(Channel channel, String message) {
-        if (message == null || message.length() == 0) {
+    public String execute(CommandContext commandContext, String[] args) {
+        if (args == null || args.length == 0) {
             return "Please input the index of the method you want to invoke, eg: \r\n select 1";
         }
-        List<Method> methodList = (List<Method>) channel.getAttribute(InvokeTelnetHandler.INVOKE_METHOD_LIST_KEY);
+        Channel channel = commandContext.getRemote();
+        String message = args[0];
+        List<Method> methodList = channel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).get();
         if (CollectionUtils.isEmpty(methodList)) {
             return "Please use the invoke command first.";
         }
@@ -52,9 +52,9 @@ public class SelectTelnetHandler implements TelnetHandler {
             return "Illegal index ,please input select 1~" + methodList.size();
         }
         Method method = methodList.get(Integer.parseInt(message) - 1);
-        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);
+        channel.attr(SELECT_METHOD_KEY).set(method);
+        channel.attr(SELECT_KEY).set(Boolean.TRUE);
+        String invokeMessage = channel.attr(InvokeTelnet.INVOKE_MESSAGE_KEY).get();
+        return invokeTelnet.execute(commandContext, new String[]{invokeMessage});
     }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ShutdownTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java
similarity index 67%
rename from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ShutdownTelnetHandler.java
rename to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java
index 26babce..2f5999d 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ShutdownTelnetHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java
@@ -14,30 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
-import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.DubboShutdownHook;
-import org.apache.dubbo.remoting.Channel;
-import org.apache.dubbo.remoting.RemotingException;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
-import org.apache.dubbo.remoting.telnet.support.Help;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
 
-/**
- * ShutdownTelnetHandler
- */
-@Activate
-@Help(parameter = "[-t <milliseconds>]", summary = "Shutdown Dubbo Application.", detail = "Shutdown Dubbo Application.")
-public class ShutdownTelnetHandler implements TelnetHandler {
+@Cmd(name = "shutdown", summary = "Shutdown Dubbo Application.", example = {
+    "shutdown -t <milliseconds>"
+})
+public class ShutdownTelnet implements BaseCommand {
     @Override
-    public String telnet(Channel channel, String message) throws RemotingException {
+    public String execute(CommandContext commandContext, String[] args) {
 
         int sleepMilliseconds = 0;
-        if (StringUtils.isNotEmpty(message)) {
-            String[] parameters = message.split("\\s+");
-            if (parameters.length == 2 && "-t".equals(parameters[0]) && StringUtils.isInteger(parameters[1])) {
-                sleepMilliseconds = Integer.parseInt(parameters[1]);
+        if (args != null && args.length > 0) {
+            if (args.length == 2 && "-t".equals(args[0]) && StringUtils.isInteger(args[1])) {
+                sleepMilliseconds = Integer.parseInt(args[1]);
             } else {
                 return "Invalid parameter,please input like shutdown -t 10000";
             }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java
deleted file mode 100644
index 02cd3b2..0000000
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.qos.legacy;
-
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.common.utils.ReflectUtils;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.qos.command.util.ServiceCheckUtils;
-import org.apache.dubbo.remoting.Channel;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
-import org.apache.dubbo.remoting.telnet.support.Help;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-import org.apache.dubbo.rpc.model.ConsumerModel;
-import org.apache.dubbo.rpc.model.MethodDescriptor;
-import org.apache.dubbo.rpc.model.ProviderModel;
-import org.apache.dubbo.rpc.model.ServiceRepository;
-
-import java.lang.reflect.Method;
-import java.util.List;
-
-/**
- * ListTelnetHandler handler list services and its methods details.
- */
-@Activate
-@Help(parameter = "[-l] [service]", summary = "List services and methods.", detail = "List services and methods.")
-public class ListTelnetHandler implements TelnetHandler {
-
-    private ServiceRepository serviceRepository = ApplicationModel.getServiceRepository();
-
-    @Override
-    public String telnet(Channel channel, String message) {
-        StringBuilder buf = new StringBuilder();
-        String service = null;
-        boolean detail = false;
-        if (message.length() > 0) {
-            String[] parts = message.split("\\s+");
-            for (String part : parts) {
-                if ("-l".equals(part)) {
-                    detail = true;
-                } else {
-                    if (!StringUtils.isEmpty(service)) {
-                        return "Invalid parameter " + part;
-                    }
-                    service = part;
-                }
-            }
-        } else {
-            service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
-            if (StringUtils.isNotEmpty(service)) {
-                buf.append("Use default service ").append(service).append(".\r\n");
-            }
-        }
-
-        if (StringUtils.isEmpty(service)) {
-            printAllServices(buf, detail);
-        } else {
-            printSpecifiedService(service, buf, detail);
-
-            if (buf.length() == 0) {
-                buf.append("No such service: ").append(service);
-            }
-        }
-        return buf.toString();
-    }
-
-    private void printAllServices(StringBuilder buf, boolean detail) {
-        printAllProvidedServices(buf, detail);
-        printAllReferredServices(buf, detail);
-    }
-
-    private void printAllProvidedServices(StringBuilder buf, boolean detail) {
-        List<ProviderModel> providerModels = serviceRepository.getExportedServices();
-        if (!providerModels.isEmpty()) {
-            buf.append("PROVIDER:\r\n");
-        }
-
-        for (ProviderModel provider : providerModels) {
-            buf.append(provider.getServiceKey());
-            if (detail) {
-                buf.append(" -> ");
-                buf.append(" published: ");
-                buf.append(ServiceCheckUtils.isRegistered(provider) ? "Y" : "N");
-            }
-            buf.append("\r\n");
-        }
-    }
-
-    private void printAllReferredServices(StringBuilder buf, boolean detail) {
-        List<ConsumerModel> consumerModels = serviceRepository.getReferredServices();
-        if (!consumerModels.isEmpty()) {
-            buf.append("CONSUMER:\r\n");
-        }
-
-        for (ConsumerModel consumer : consumerModels) {
-            buf.append(consumer.getServiceKey());
-            if (detail) {
-                buf.append(" -> ");
-                buf.append(" addresses: ");
-                buf.append(ServiceCheckUtils.getConsumerAddressNum(consumer));
-            }
-            buf.append("\r\n");
-        }
-    }
-
-    private void printSpecifiedService(String service, StringBuilder buf, boolean detail) {
-        printSpecifiedProvidedService(service, buf, detail);
-        printSpecifiedReferredService(service, buf, detail);
-    }
-
-    private void printSpecifiedProvidedService(String service, StringBuilder buf, boolean detail) {
-        for (ProviderModel provider : ApplicationModel.allProviderModels()) {
-            if (isProviderMatched(service,provider)) {
-                buf.append(provider.getServiceKey()).append(" (as provider):\r\n");
-                for (MethodDescriptor method : provider.getAllMethods()) {
-                    printMethod(method.getMethod(), buf, detail);
-                }
-            }
-        }
-    }
-
-    private void printSpecifiedReferredService(String service, StringBuilder buf, boolean detail) {
-        for (ConsumerModel consumer : ApplicationModel.allConsumerModels()) {
-            if (isConsumerMatcher(service,consumer)) {
-                buf.append(consumer.getServiceKey()).append(" (as consumer):\r\n");
-                for (MethodDescriptor method : consumer.getAllMethods()) {
-                    printMethod(method.getMethod(), buf, detail);
-                }
-            }
-        }
-    }
-
-    private void printMethod(Method method, StringBuilder buf, boolean detail) {
-        if (detail) {
-            buf.append('\t').append(ReflectUtils.getName(method));
-        } else {
-            buf.append('\t').append(method.getName());
-        }
-        buf.append("\r\n");
-    }
-
-    private boolean isProviderMatched(String service, ProviderModel provider) {
-        return service.equalsIgnoreCase(provider.getServiceKey())
-                || service.equalsIgnoreCase(provider.getServiceInterfaceClass().getName())
-                || service.equalsIgnoreCase(provider.getServiceInterfaceClass().getSimpleName());
-    }
-
-    private boolean isConsumerMatcher(String service,ConsumerModel consumer) {
-        return service.equalsIgnoreCase(consumer.getServiceKey())
-                || service.equalsIgnoreCase(consumer.getServiceInterfaceClass().getName())
-                || service.equalsIgnoreCase(consumer.getServiceInterfaceClass().getSimpleName());
-    }
-}
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
index 089762d..78f5337 100644
--- a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
@@ -12,3 +12,10 @@ startup=org.apache.dubbo.qos.command.impl.Startup
 live=org.apache.dubbo.qos.command.impl.Live
 version=org.apache.dubbo.qos.command.impl.Version
 publish-metadata=org.apache.dubbo.qos.command.impl.PublishMetadata
+cd=org.apache.dubbo.qos.command.impl.ChangeTelnet
+count=org.apache.dubbo.qos.command.impl.CountTelnet
+pwd=org.apache.dubbo.qos.command.impl.PwdTelnet
+invoke=org.apache.dubbo.qos.command.impl.InvokeTelnet
+select=org.apache.dubbo.qos.command.impl.SelectTelnet
+ps=org.apache.dubbo.qos.command.impl.PortTelnet
+shutdown=org.apache.dubbo.qos.command.impl.ShutdownTelnet
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler
index 0ce7b09..965c8e3 100644
--- a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler
@@ -1,9 +1 @@
-ls=org.apache.dubbo.qos.legacy.ListTelnetHandler
-ps=org.apache.dubbo.qos.legacy.PortTelnetHandler
-cd=org.apache.dubbo.qos.legacy.ChangeTelnetHandler
-pwd=org.apache.dubbo.qos.legacy.CurrentTelnetHandler
-invoke=org.apache.dubbo.qos.legacy.InvokeTelnetHandler
 trace=org.apache.dubbo.qos.legacy.TraceTelnetHandler
-count=org.apache.dubbo.qos.legacy.CountTelnetHandler
-select=org.apache.dubbo.qos.legacy.SelectTelnetHandler
-shutdown=org.apache.dubbo.qos.legacy.ShutdownTelnetHandler
\ No newline at end of file
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ChangeTelnetTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ChangeTelnetTest.java
new file mode 100644
index 0000000..81e17a9
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ChangeTelnetTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.legacy.ProtocolUtils;
+import org.apache.dubbo.qos.legacy.service.DemoService;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+import io.netty.channel.Channel;
+import io.netty.util.DefaultAttributeMap;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+
+public class ChangeTelnetTest {
+    private final DefaultAttributeMap defaultAttributeMap = new DefaultAttributeMap();
+    private static final BaseCommand change = new ChangeTelnet();
+
+    private Channel mockChannel;
+    private CommandContext mockCommandContext;
+    private Invoker<DemoService> mockInvoker;
+
+    @AfterAll
+    public static void tearDown() {
+
+    }
+
+    @SuppressWarnings("unchecked")
+    @BeforeEach
+    public void setUp() {
+        mockCommandContext = mock(CommandContext.class);
+        mockChannel = mock(Channel.class);
+        mockInvoker = mock(Invoker.class);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        mockChannel.attr(ChangeTelnet.SERVICE_KEY).set("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService");
+        given(mockCommandContext.getRemote()).willReturn(mockChannel);
+        given(mockInvoker.getInterface()).willReturn(DemoService.class);
+        given(mockInvoker.getUrl()).willReturn(URL.valueOf("dubbo://127.0.0.1:20884/demo"));
+    }
+
+    @AfterEach
+    public void after() {
+        ProtocolUtils.closeAll();
+        reset(mockCommandContext, mockChannel, mockInvoker);
+    }
+
+    @Test
+    public void testChangeSimpleName() {
+        DubboProtocol.getDubboProtocol().export(mockInvoker);
+        String result = change.execute(mockCommandContext, new String[]{"DemoService"});
+        assertEquals("Used the DemoService as default.\r\nYou can cancel default service by command: cd /", result);
+    }
+
+    @Test
+    public void testChangeName() {
+        DubboProtocol.getDubboProtocol().export(mockInvoker);
+        String result = change.execute(mockCommandContext, new String[]{"org.apache.dubbo.qos.legacy.service.DemoService"});
+        assertEquals("Used the org.apache.dubbo.qos.legacy.service.DemoService as default.\r\nYou can cancel default service by command: cd /",
+            result);
+    }
+
+    @Test
+    public void testChangePath() {
+        DubboProtocol.getDubboProtocol().export(mockInvoker);
+        String result = change.execute(mockCommandContext, new String[]{"demo"});
+        assertEquals("Used the demo as default.\r\nYou can cancel default service by command: cd /", result);
+    }
+
+    @Test
+    public void testChangeMessageNull() {
+        String result = change.execute(mockCommandContext, null);
+        assertEquals("Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService", result);
+    }
+
+    @Test
+    public void testChangeServiceNotExport() {
+        String result = change.execute(mockCommandContext, new String[]{"demo"});
+        assertEquals("No such service demo", result);
+    }
+
+    @Test
+    public void testChangeCancel() {
+        String result = change.execute(mockCommandContext, new String[]{".."});
+        assertEquals("Cancelled default service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService.", result);
+    }
+
+    @Test
+    public void testChangeCancel2() {
+        String result = change.execute(mockCommandContext, new String[]{"/"});
+        assertEquals("Cancelled default service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService.", result);
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/CountTelnetHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/CountTelnetTest.java
similarity index 77%
rename from dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/CountTelnetHandlerTest.java
rename to dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/CountTelnetTest.java
index 1d6f77a..71c1f85 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/CountTelnetHandlerTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/CountTelnetTest.java
@@ -14,16 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.qos.legacy.channel.MockChannel;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.impl.channel.MockNettyChannel;
+import org.apache.dubbo.qos.legacy.ProtocolUtils;
 import org.apache.dubbo.qos.legacy.service.DemoService;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
 import org.apache.dubbo.remoting.telnet.support.TelnetUtils;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.RpcStatus;
 import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -38,39 +41,45 @@ import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 
-public class CountTelnetHandlerTest {
+public class CountTelnetTest {
+    private static final BaseCommand count = new CountTelnet();
 
-    private TelnetHandler handler = new CountTelnetHandler();
-    private MockChannel mockChannel;
+    private MockNettyChannel mockChannel;
     private Invoker<DemoService> mockInvoker;
-    private URL url = URL.valueOf("dubbo://127.0.0.1:20884/demo");
+
+    private CommandContext mockCommandContext;
+
     private CountDownLatch latch;
+    private final URL url = URL.valueOf("dubbo://127.0.0.1:20884/demo");
 
     @BeforeEach
     public void setUp() {
         latch = new CountDownLatch(2);
-        mockChannel = new MockChannel(url, latch);
         mockInvoker = mock(Invoker.class);
+        mockCommandContext = mock(CommandContext.class);
+        mockChannel = new MockNettyChannel(url, latch);
+        given(mockCommandContext.getRemote()).willReturn(mockChannel);
         given(mockInvoker.getInterface()).willReturn(DemoService.class);
         given(mockInvoker.getUrl()).willReturn(url);
+
     }
 
     @AfterEach
     public void tearDown() {
         ProtocolUtils.closeAll();
         mockChannel.close();
-        reset(mockInvoker);
+        reset(mockInvoker, mockCommandContext);
     }
 
     @Test
     public void test() throws Exception {
         String methodName = "sayHello";
-        String message = "org.apache.dubbo.qos.legacy.service.DemoService sayHello 1";
+        String[] args = new String[]{"org.apache.dubbo.qos.legacy.service.DemoService", "sayHello", "1"};
 
         DubboProtocol.getDubboProtocol().export(mockInvoker);
         RpcStatus.beginCount(url, methodName);
         RpcStatus.endCount(url, methodName, 10L, true);
-        handler.telnet(mockChannel, message);
+        count.execute(mockCommandContext, args);
         latch.await();
 
         StringBuilder sb = new StringBuilder();
@@ -79,7 +88,7 @@ public class CountTelnetHandlerTest {
         }
 
         assertThat(sb.toString(), containsString(buildTable(methodName,
-                10, 10, "1", "0", "0")));
+            10, 10, "1", "0", "0")));
     }
 
     public static String buildTable(String methodName, long averageElapsed,
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/InvokeTelnetTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/InvokeTelnetTest.java
new file mode 100644
index 0000000..a842b4b
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/InvokeTelnetTest.java
@@ -0,0 +1,257 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.legacy.ProtocolUtils;
+import org.apache.dubbo.qos.legacy.service.DemoService;
+import org.apache.dubbo.qos.legacy.service.DemoServiceImpl;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.model.ServiceRepository;
+
+import io.netty.channel.Channel;
+import io.netty.util.DefaultAttributeMap;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+
+public class InvokeTelnetTest {
+
+    private static final BaseCommand invoke = new InvokeTelnet();
+    private static final BaseCommand select = new SelectTelnet();
+    private Channel mockChannel;
+    private CommandContext mockCommandContext;
+    private final DefaultAttributeMap defaultAttributeMap = new DefaultAttributeMap();
+    private final ServiceRepository repository = ApplicationModel.getServiceRepository();
+
+    @BeforeEach
+    public void setup() {
+        DubboBootstrap.reset();
+        mockChannel = mock(Channel.class);
+        mockCommandContext = mock(CommandContext.class);
+        given(mockCommandContext.getRemote()).willReturn(mockChannel);
+    }
+
+    @AfterEach
+    public void after() {
+        ProtocolUtils.closeAll();
+        DubboBootstrap.reset();
+        reset(mockChannel, mockCommandContext);
+    }
+
+    @Test
+    public void testInvokeDefaultService() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName());
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
+        String result = invoke.execute(mockCommandContext, new String[]{"echo(\"ok\")"});
+        assertTrue(result.contains("result: \"ok\""));
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+    }
+
+    @Test
+    public void testInvokeWithSpecifyService() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null);
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+
+        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
+        String result = invoke.execute(mockCommandContext, new String[]{"DemoService.echo(\"ok\")"});
+        assertTrue(result.contains("result: \"ok\""));
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+    }
+
+    @Test
+    public void testInvokeByPassingNullValue() {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName());
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+
+        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
+        try {
+            invoke.execute(mockCommandContext, new String[]{"sayHello(null)"});
+        } catch (Exception ex) {
+            assertTrue(ex instanceof NullPointerException);
+        }
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+    }
+
+    @Test
+    public void testInvokeByPassingEnumValue() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null);
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+
+        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
+
+        String result = invoke.execute(mockCommandContext, new String[]{"getType(\"High\")"});
+        assertTrue(result.contains("result: \"High\""));
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+    }
+
+    @Test
+    public void testOverriddenMethodWithSpecifyParamType() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName());
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+
+        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
+
+        String result = invoke.execute(mockCommandContext,
+            new String[]{"getPerson({\"name\":\"zhangsan\",\"age\":12,\"class\":\"org.apache.dubbo.qos.legacy.service.Person\"})"});
+        assertTrue(result.contains("result: 12"));
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+    }
+
+    @Test
+    public void testInvokeOverriddenMethodBySelect() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName());
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+        defaultAttributeMap.attr(SelectTelnet.SELECT_METHOD_KEY).set(null);
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_PROVIDER_KEY).set(null);
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).set(null);
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_MESSAGE_KEY).set(null);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_METHOD_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_METHOD_KEY));
+        given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_PROVIDER_KEY)).willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_PROVIDER_KEY));
+        given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)).willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY));
+        given(mockChannel.attr(InvokeTelnet.INVOKE_MESSAGE_KEY)).willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_MESSAGE_KEY));
+
+        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
+
+        String param = "{\"name\":\"Dubbo\",\"age\":8}";
+        String result = invoke.execute(mockCommandContext, new String[]{"getPerson(" + param + ")"});
+        assertTrue(result.contains("Please use the select command to select the method you want to invoke. eg: select 1"));
+        result = select.execute(mockCommandContext, new String[]{"1"});
+        //result dependent on method order.
+        assertTrue(result.contains("result: 8") || result.contains("result: \"Dubbo\""));
+        result = select.execute(mockCommandContext, new String[]{"2"});
+        assertTrue(result.contains("result: 8") || result.contains("result: \"Dubbo\""));
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_METHOD_KEY).remove();
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_PROVIDER_KEY).remove();
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).remove();
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_MESSAGE_KEY).remove();
+    }
+
+    @Test
+    public void testInvokeMethodWithMapParameter() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName());
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+
+        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
+
+        String param = "{1:\"Dubbo\",2:\"test\"}";
+        String result = invoke.execute(mockCommandContext, new String[]{"getMap(" + param + ")"});
+        assertTrue(result.contains("result: {1:\"Dubbo\",2:\"test\"}"));
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+    }
+
+    @Test
+    public void testInvokeMultiJsonParamMethod() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName());
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+
+        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
+
+        String param = "{\"name\":\"Dubbo\",\"age\":8},{\"name\":\"Apache\",\"age\":20}";
+        String result = invoke.execute(mockCommandContext, new String[]{"getPerson(" + param + ")"});
+        assertTrue(result.contains("result: 28"));
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+    }
+
+    @Test
+    public void testMessageNull() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null);
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+
+        String result = invoke.execute(mockCommandContext, new String[0]);
+        assertEquals("Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})",
+            result);
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+    }
+
+    @Test
+    public void testInvalidMessage() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null);
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY));
+
+        String result = invoke.execute(mockCommandContext, new String[]{"("});
+        assertEquals("Invalid parameters, format: service.method(args)", result);
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove();
+    }
+
+    private void registerProvider(String key, Object impl, Class<?> interfaceClass) {
+        ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
+        repository.registerProvider(
+            key,
+            impl,
+            serviceDescriptor,
+            null,
+            null
+        );
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/PortTelnetHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PortTelnetTest.java
similarity index 74%
rename from dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/PortTelnetHandlerTest.java
rename to dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PortTelnetTest.java
index 37c018c..1b61ed6 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/PortTelnetHandlerTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PortTelnetTest.java
@@ -14,39 +14,42 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.legacy.ProtocolUtils;
 import org.apache.dubbo.qos.legacy.service.DemoService;
 import org.apache.dubbo.remoting.RemotingException;
 import org.apache.dubbo.remoting.exchange.ExchangeClient;
 import org.apache.dubbo.remoting.exchange.Exchangers;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
 
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 
-/**
- * PortTelnetHandlerTest.java
- */
-public class PortTelnetHandlerTest {
+public class PortTelnetTest {
+    private static final BaseCommand port = new PortTelnet();
+
+    private Invoker<DemoService> mockInvoker;
+    private CommandContext mockCommandContext;
 
-    private static TelnetHandler port = new PortTelnetHandler();
-    private static Invoker<DemoService> mockInvoker;
-    private static int availablePort = NetUtils.getAvailablePort();
+    private static final int availablePort = NetUtils.getAvailablePort();
 
     @SuppressWarnings("unchecked")
-    @BeforeAll
-    public static void before() {
+    @BeforeEach
+    public void before() {
+        mockCommandContext = mock(CommandContext.class);
         mockInvoker = mock(Invoker.class);
         given(mockInvoker.getInterface()).willReturn(DemoService.class);
         given(mockInvoker.getUrl()).willReturn(URL.valueOf("dubbo://127.0.0.1:" + availablePort + "/demo"));
@@ -54,9 +57,10 @@ public class PortTelnetHandlerTest {
         DubboProtocol.getDubboProtocol().export(mockInvoker);
     }
 
-    @AfterAll
-    public static void after() {
+    @AfterEach
+    public void after() {
         ProtocolUtils.closeAll();
+        reset(mockInvoker, mockCommandContext);
     }
 
     /**
@@ -68,7 +72,7 @@ public class PortTelnetHandlerTest {
         ExchangeClient client1 = Exchangers.connect("dubbo://127.0.0.1:" + availablePort + "/demo");
         ExchangeClient client2 = Exchangers.connect("dubbo://127.0.0.1:" + availablePort + "/demo");
         Thread.sleep(5000);
-        String result = port.telnet(null, "-l " + availablePort + "");
+        String result = port.execute(mockCommandContext, new String[]{"-l", availablePort + ""});
         String client1Addr = client1.getLocalAddress().toString();
         String client2Addr = client2.getLocalAddress().toString();
         System.out.printf("Result: %s %n", result);
@@ -80,26 +84,25 @@ public class PortTelnetHandlerTest {
 
     @Test
     public void testListDetail() throws RemotingException {
-        String result = port.telnet(null, "-l");
+        String result = port.execute(mockCommandContext, new String[]{"-l"});
         assertEquals("dubbo://127.0.0.1:" + availablePort + "", result);
     }
 
     @Test
     public void testListAllPort() throws RemotingException {
-        String result = port.telnet(null, "");
+        String result = port.execute(mockCommandContext, new String[0]);
         assertEquals("" + availablePort + "", result);
     }
 
     @Test
     public void testErrorMessage() throws RemotingException {
-        String result = port.telnet(null, "a");
+        String result = port.execute(mockCommandContext, new String[]{"a"});
         assertEquals("Illegal port a, must be integer.", result);
     }
 
     @Test
     public void testNoPort() throws RemotingException {
-        String result = port.telnet(null, "-l 20880");
+        String result = port.execute(mockCommandContext, new String[]{"-l", "20880"});
         assertEquals("No such port 20880", result);
     }
-
 }
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PwdTelnetTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PwdTelnetTest.java
new file mode 100644
index 0000000..eb5046a
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PwdTelnetTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.legacy.ProtocolUtils;
+import org.apache.dubbo.remoting.RemotingException;
+
+import io.netty.channel.Channel;
+import io.netty.util.DefaultAttributeMap;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+
+public class PwdTelnetTest {
+    private static final BaseCommand pwdTelnet = new PwdTelnet();
+    private Channel mockChannel;
+    private CommandContext mockCommandContext;
+
+    private final DefaultAttributeMap defaultAttributeMap = new DefaultAttributeMap();
+
+    @BeforeEach
+    public void setUp() {
+        mockChannel = mock(Channel.class);
+        mockCommandContext = mock(CommandContext.class);
+        given(mockCommandContext.getRemote()).willReturn(mockChannel);
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+    }
+
+    @AfterEach
+    public void tearDown() {
+        ProtocolUtils.closeAll();
+        mockChannel.close();
+        reset(mockChannel, mockCommandContext);
+    }
+
+    @Test
+    public void testService() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService");
+        String result = pwdTelnet.execute(mockCommandContext, new String[0]);
+        assertEquals("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", result);
+    }
+
+    @Test
+    public void testSlash() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null);
+        String result = pwdTelnet.execute(mockCommandContext, new String[0]);
+        assertEquals("/", result);
+    }
+
+    @Test
+    public void testMessageError() throws RemotingException {
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null);
+        String result = pwdTelnet.execute(mockCommandContext, new String[]{"test"});
+        assertEquals("Unsupported parameter [test] for pwd.", result);
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/SelectTelnetHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SelectTelnetTest.java
similarity index 52%
rename from dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/SelectTelnetHandlerTest.java
rename to dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SelectTelnetTest.java
index c516c84..03476eb 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/SelectTelnetHandlerTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SelectTelnetTest.java
@@ -14,19 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
-import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.legacy.ProtocolUtils;
 import org.apache.dubbo.qos.legacy.service.DemoService;
 import org.apache.dubbo.qos.legacy.service.DemoServiceImpl;
-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.ServiceDescriptor;
 import org.apache.dubbo.rpc.model.ServiceRepository;
 
+import io.netty.channel.Channel;
+import io.netty.util.DefaultAttributeMap;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -38,16 +40,18 @@ import java.util.List;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 
-/**
- * SelectTelnetHandlerTest.java
- */
-public class SelectTelnetHandlerTest {
+public class SelectTelnetTest {
+
+    private static BaseCommand select = new SelectTelnet();
 
-    private static TelnetHandler select = new SelectTelnetHandler();
     private Channel mockChannel;
-    List<Method> methods;
+    private CommandContext mockCommandContext;
+
     private final ServiceRepository repository = ApplicationModel.getServiceRepository();
+    private final DefaultAttributeMap defaultAttributeMap = new DefaultAttributeMap();
+    private List<Method> methods;
 
     @BeforeEach
     public void setup() {
@@ -60,68 +64,82 @@ public class SelectTelnetHandlerTest {
         }
 
         DubboBootstrap.reset();
+        mockChannel = mock(Channel.class);
+        mockCommandContext = mock(CommandContext.class);
+        given(mockCommandContext.getRemote()).willReturn(mockChannel);
     }
 
     @AfterEach
     public void after() {
         ProtocolUtils.closeAll();
+        reset(mockChannel, mockCommandContext);
     }
 
     @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"));
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName());
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).set(null);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)).willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY));
 
         registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
 
-        String result = select.telnet(mockChannel, "1");
+        String result = select.execute(mockCommandContext, new String[]{"1"});
         assertTrue(result.contains("Please use the invoke command first."));
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).remove();
     }
 
     @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"));
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName());
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).set(methods);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)).willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY));
 
         registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
 
-        String result = select.telnet(mockChannel, "index");
+        String result = select.execute(mockCommandContext, new String[]{"index"});
         assertTrue(result.contains("Illegal index ,please input select 1"));
 
-        result = select.telnet(mockChannel, "0");
+        result = select.execute(mockCommandContext, new String[]{"0"});
         assertTrue(result.contains("Illegal index ,please input select 1"));
 
-        result = select.telnet(mockChannel, "1000");
+        result = select.execute(mockCommandContext, new String[]{"1000"});
         assertTrue(result.contains("Illegal index ,please input select 1"));
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).remove();
     }
 
     @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"));
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName());
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).set(methods);
+
+        given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)).willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY));
+        given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)).willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY));
 
         registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
 
-        String result = select.telnet(mockChannel, null);
+        String result = select.execute(mockCommandContext, new String[0]);
         assertTrue(result.contains("Please input the index of the method you want to invoke"));
+
+        defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove();
+        defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).remove();
     }
 
     private void registerProvider(String key, Object impl, Class<?> interfaceClass) {
         ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
         repository.registerProvider(
-                key,
-                impl,
-                serviceDescriptor,
-                null,
-                null
+            key,
+            impl,
+            serviceDescriptor,
+            null,
+            null
         );
     }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/ShutdownTelnetHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ShutdownTelnetTest.java
similarity index 61%
rename from dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/ShutdownTelnetHandlerTest.java
rename to dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ShutdownTelnetTest.java
index 7e8ccd6..cc52ba3 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/ShutdownTelnetHandlerTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ShutdownTelnetTest.java
@@ -14,45 +14,55 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.legacy;
+package org.apache.dubbo.qos.command.impl;
 
-import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.legacy.ProtocolUtils;
 import org.apache.dubbo.remoting.RemotingException;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
 
+import io.netty.channel.Channel;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 
-/**
- * SelectTelnetHandlerTest.java
- */
-public class ShutdownTelnetHandlerTest {
+public class ShutdownTelnetTest {
 
-    private static TelnetHandler handler = new ShutdownTelnetHandler();
+    private static final BaseCommand shutdown = new ShutdownTelnet();
     private Channel mockChannel;
+    private CommandContext mockCommandContext;
+
+    @BeforeEach
+    public void setUp() {
+        mockCommandContext = mock(CommandContext.class);
+        mockChannel = mock(Channel.class);
+        given(mockCommandContext.getRemote()).willReturn(mockChannel);
+    }
+
+    @AfterEach
+    public void after() {
+        ProtocolUtils.closeAll();
+        reset(mockChannel, mockCommandContext);
+    }
 
-    @SuppressWarnings("unchecked")
     @Test
     public void testInvoke() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        String result = handler.telnet(mockChannel, "");
+        String result = shutdown.execute(mockCommandContext, new String[0]);
         assertTrue(result.contains("Application has shutdown successfully"));
     }
 
-
-    @SuppressWarnings("unchecked")
     @Test
     public void testInvokeWithTimeParameter() throws RemotingException {
-        mockChannel = mock(Channel.class);
         int sleepTime = 2000;
         long start = System.currentTimeMillis();
-        String result = handler.telnet(mockChannel, "-t " + sleepTime);
+        String result = shutdown.execute(mockCommandContext, new String[]{"-t", "" + sleepTime});
         long end = System.currentTimeMillis();
         assertTrue(result.contains("Application has shutdown successfully"), result);
         assertTrue((end - start) > sleepTime, "sleepTime: " + sleepTime + ", execTime: " + (end - start));
     }
-
-
 }
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/channel/MockNettyChannel.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/channel/MockNettyChannel.java
new file mode 100644
index 0000000..38a7b0c
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/channel/MockNettyChannel.java
@@ -0,0 +1,439 @@
+/*
+ * 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.qos.command.impl.channel;
+
+import org.apache.dubbo.common.URL;
+
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelConfig;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelId;
+import io.netty.channel.ChannelMetadata;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.ChannelProgressivePromise;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.EventLoop;
+import io.netty.util.Attribute;
+import io.netty.util.AttributeKey;
+import io.netty.util.AttributeMap;
+import io.netty.util.DefaultAttributeMap;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class MockNettyChannel implements Channel {
+
+    InetSocketAddress localAddress;
+    InetSocketAddress remoteAddress;
+    private URL remoteUrl;
+    private List<Object> receivedObjects = new LinkedList<>();
+    public static final String ERROR_WHEN_SEND = "error_when_send";
+    private CountDownLatch latch;
+    private AttributeMap attributeMap =  new DefaultAttributeMap();
+
+    public MockNettyChannel(URL remoteUrl, CountDownLatch latch) {
+        this.remoteUrl = remoteUrl;
+        this.latch = latch;
+    }
+
+    @Override
+    public ChannelFuture writeAndFlush(Object msg) {
+        receivedObjects.add(msg);
+        if (latch != null) {
+            latch.countDown();
+        }
+        return newPromise();
+    }
+
+    @Override
+    public ChannelPromise newPromise() {
+        return new ChannelPromise() {
+            @Override
+            public Channel channel() {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise setSuccess(Void result) {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise setSuccess() {
+                return null;
+            }
+
+            @Override
+            public boolean trySuccess() {
+                return false;
+            }
+
+            @Override
+            public ChannelPromise setFailure(Throwable cause) {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise addListener(GenericFutureListener<? extends Future<? super Void>> listener) {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise addListeners(GenericFutureListener<? extends Future<? super Void>>... listeners) {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise removeListener(GenericFutureListener<? extends Future<? super Void>> listener) {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise removeListeners(GenericFutureListener<? extends Future<? super Void>>... listeners) {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise sync() throws InterruptedException {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise syncUninterruptibly() {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise await() throws InterruptedException {
+                return this;
+            }
+
+            @Override
+            public ChannelPromise awaitUninterruptibly() {
+                return null;
+            }
+
+            @Override
+            public ChannelPromise unvoid() {
+                return null;
+            }
+
+            @Override
+            public boolean isVoid() {
+                return false;
+            }
+
+            @Override
+            public boolean trySuccess(Void result) {
+                return false;
+            }
+
+            @Override
+            public boolean tryFailure(Throwable cause) {
+                return false;
+            }
+
+            @Override
+            public boolean setUncancellable() {
+                return false;
+            }
+
+            @Override
+            public boolean isSuccess() {
+                return false;
+            }
+
+            @Override
+            public boolean isCancellable() {
+                return false;
+            }
+
+            @Override
+            public Throwable cause() {
+                return null;
+            }
+
+            @Override
+            public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
+                return true;
+            }
+
+            @Override
+            public boolean await(long timeoutMillis) throws InterruptedException {
+                return true;
+            }
+
+            @Override
+            public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
+                return true;
+            }
+
+            @Override
+            public boolean awaitUninterruptibly(long timeoutMillis) {
+                return false;
+            }
+
+            @Override
+            public Void getNow() {
+                return null;
+            }
+
+            @Override
+            public boolean cancel(boolean mayInterruptIfRunning) {
+                return false;
+            }
+
+            @Override
+            public boolean isCancelled() {
+                return false;
+            }
+
+            @Override
+            public boolean isDone() {
+                return false;
+            }
+
+            @Override
+            public Void get() throws InterruptedException, ExecutionException {
+                return null;
+            }
+
+            @Override
+            public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+                return null;
+            }
+        };
+    }
+
+    @Override
+    public ChannelProgressivePromise newProgressivePromise() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture newSucceededFuture() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture newFailedFuture(Throwable cause) {
+        return null;
+    }
+
+    @Override
+    public ChannelPromise voidPromise() {
+        return null;
+    }
+
+    @Override
+    public ChannelId id() {
+        return null;
+    }
+
+    @Override
+    public EventLoop eventLoop() {
+        return null;
+    }
+
+    @Override
+    public Channel parent() {
+        return null;
+    }
+
+    @Override
+    public ChannelConfig config() {
+        return null;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return false;
+    }
+
+    @Override
+    public boolean isRegistered() {
+        return false;
+    }
+
+    @Override
+    public boolean isActive() {
+        return false;
+    }
+
+    @Override
+    public ChannelMetadata metadata() {
+        return null;
+    }
+
+    @Override
+    public SocketAddress localAddress() {
+        return null;
+    }
+
+    @Override
+    public SocketAddress remoteAddress() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture closeFuture() {
+        return null;
+    }
+
+    @Override
+    public boolean isWritable() {
+        return false;
+    }
+
+    @Override
+    public long bytesBeforeUnwritable() {
+        return 0;
+    }
+
+    @Override
+    public long bytesBeforeWritable() {
+        return 0;
+    }
+
+    @Override
+    public Unsafe unsafe() {
+        return null;
+    }
+
+    @Override
+    public ChannelPipeline pipeline() {
+        return null;
+    }
+
+    @Override
+    public ByteBufAllocator alloc() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture bind(SocketAddress localAddress) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture connect(SocketAddress remoteAddress) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture disconnect() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture close() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture deregister() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture disconnect(ChannelPromise promise) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture close(ChannelPromise promise) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture deregister(ChannelPromise promise) {
+        return null;
+    }
+
+    @Override
+    public Channel read() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture write(Object msg) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture write(Object msg, ChannelPromise promise) {
+        return null;
+    }
+
+    @Override
+    public Channel flush() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
+        return null;
+    }
+
+    @Override
+    public <T> Attribute<T> attr(AttributeKey<T> key) {
+        return attributeMap.attr(key);
+    }
+
+    @Override
+    public <T> boolean hasAttr(AttributeKey<T> key) {
+        return attributeMap.hasAttr(key);
+    }
+
+    @Override
+    public int compareTo(Channel o) {
+        return 0;
+    }
+
+    public List<Object> getReceivedObjects() {
+        return receivedObjects;
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
index dad505a..f2719e9 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
@@ -17,7 +17,11 @@
 package org.apache.dubbo.qos.command.util;
 
 import org.apache.dubbo.qos.command.GreetingCommand;
+import org.apache.dubbo.qos.command.impl.ChangeTelnet;
+import org.apache.dubbo.qos.command.impl.CountTelnet;
 import org.apache.dubbo.qos.command.impl.Help;
+import org.apache.dubbo.qos.command.impl.InvokeTelnet;
+import org.apache.dubbo.qos.command.impl.InvokeTelnetTest;
 import org.apache.dubbo.qos.command.impl.Live;
 import org.apache.dubbo.qos.command.impl.Ls;
 import org.apache.dubbo.qos.command.impl.Offline;
@@ -26,9 +30,13 @@ import org.apache.dubbo.qos.command.impl.OfflineInterface;
 import org.apache.dubbo.qos.command.impl.Online;
 import org.apache.dubbo.qos.command.impl.OnlineApp;
 import org.apache.dubbo.qos.command.impl.OnlineInterface;
+import org.apache.dubbo.qos.command.impl.PortTelnet;
 import org.apache.dubbo.qos.command.impl.PublishMetadata;
+import org.apache.dubbo.qos.command.impl.PwdTelnet;
 import org.apache.dubbo.qos.command.impl.Quit;
 import org.apache.dubbo.qos.command.impl.Ready;
+import org.apache.dubbo.qos.command.impl.SelectTelnet;
+import org.apache.dubbo.qos.command.impl.ShutdownTelnet;
 import org.apache.dubbo.qos.command.impl.Startup;
 import org.apache.dubbo.qos.command.impl.Version;
 
@@ -72,6 +80,13 @@ public class CommandHelperTest {
         expectedClasses.add(Ready.class);
         expectedClasses.add(Startup.class);
         expectedClasses.add(Version.class);
+        expectedClasses.add(ChangeTelnet.class);
+        expectedClasses.add(CountTelnet.class);
+        expectedClasses.add(InvokeTelnet.class);
+        expectedClasses.add(SelectTelnet.class);
+        expectedClasses.add(PortTelnet.class);
+        expectedClasses.add(PwdTelnet.class);
+        expectedClasses.add(ShutdownTelnet.class);
         assertThat(classes, containsInAnyOrder(expectedClasses.toArray(new Class<?>[0])));
     }
 
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/CurrentTelnetHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/CurrentTelnetHandlerTest.java
deleted file mode 100644
index 10728ba..0000000
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/CurrentTelnetHandlerTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.qos.legacy;
-
-import org.apache.dubbo.remoting.Channel;
-import org.apache.dubbo.remoting.RemotingException;
-import org.apache.dubbo.remoting.telnet.TelnetHandler;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.mock;
-
-/**
- * CountTelnetHandlerTest.java
- */
-public class CurrentTelnetHandlerTest {
-
-    private static TelnetHandler count = new CurrentTelnetHandler();
-    private Channel mockChannel;
-
-    @Test
-    public void testService() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService");
-
-        String result = count.telnet(mockChannel, "");
-        assertEquals("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", result);
-    }
-
-    @Test
-    public void testSlash() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn(null);
-
-        String result = count.telnet(mockChannel, "");
-        assertEquals("/", result);
-    }
-
-    @Test
-    public void testMessageError() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn(null);
-
-        String result = count.telnet(mockChannel, "test");
-        assertEquals("Unsupported parameter test for pwd.", result);
-    }
-}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/InvokerTelnetHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/InvokerTelnetHandlerTest.java
deleted file mode 100644
index f5dab56..0000000
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/InvokerTelnetHandlerTest.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * 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.qos.legacy;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.utils.NetUtils;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.legacy.service.DemoService;
-import org.apache.dubbo.qos.legacy.service.DemoServiceImpl;
-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;
-import org.apache.dubbo.rpc.model.ServiceDescriptor;
-import org.apache.dubbo.rpc.model.ServiceRepository;
-
-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.mockito.BDDMockito.given;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-
-/**
- * InvokeTelnetHandlerTest.java
- */
-public class InvokerTelnetHandlerTest {
-
-    private static TelnetHandler invoke = new InvokeTelnetHandler();
-    private static TelnetHandler select = new SelectTelnetHandler();
-    private Channel mockChannel;
-    private final ServiceRepository repository = ApplicationModel.getServiceRepository();
-
-    @BeforeEach
-    public void setup() {
-        DubboBootstrap.reset();
-    }
-
-    @AfterEach
-    public void after() {
-        ProtocolUtils.closeAll();
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testInvokeDefaultService() 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"));
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String result = invoke.telnet(mockChannel, "echo(\"ok\")");
-        assertTrue(result.contains("result: \"ok\""));
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    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"));
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String result = invoke.telnet(mockChannel, "DemoService.echo(\"ok\")");
-        assertTrue(result.contains("result: \"ok\""));
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testInvokeByPassingNullValue() {
-        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"));
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-        try {
-            invoke.telnet(mockChannel, "sayHello(null)");
-        } catch (Exception ex) {
-            assertTrue(ex instanceof NullPointerException);
-        }
-    }
-
-    @Test
-    public void testInvokeByPassingEnumValue() 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"));
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String result = invoke.telnet(mockChannel, "getType(\"High\")");
-        assertTrue(result.contains("result: \"High\""));
-    }
-
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testOverriddenMethodWithSpecifyParamType() 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"));
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String result = invoke.telnet(mockChannel, "getPerson({\"name\":\"zhangsan\",\"age\":12,\"class\":\"org.apache.dubbo.qos.legacy.service.Person\"})");
-        assertTrue(result.contains("result: 12"));
-    }
-
-    @Test
-    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"));
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        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\""));
-        result = select.telnet(mockChannel, "2");
-        assertTrue(result.contains("result: 8") || result.contains("result: \"Dubbo\""));
-    }
-
-    @Test
-    public void testInvokeMethodWithMapParameter() 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"));
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String param = "{1:\"Dubbo\",2:\"test\"}";
-        String result = invoke.telnet(mockChannel, "getMap(" + param + ")");
-        assertTrue(result.contains("result: {1:\"Dubbo\",2:\"test\"}"));
-    }
-
-    @Test
-    public void testInvokeMultiJsonParamMethod() 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"));
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String param = "{\"name\":\"Dubbo\",\"age\":8},{\"name\":\"Apache\",\"age\":20}";
-        String result = invoke.telnet(mockChannel, "getPerson(" + param + ")");
-        assertTrue(result.contains("result: 28"));
-    }
-
-    @Test
-    public void testMessageNull() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn(null);
-
-        String result = invoke.telnet(mockChannel, null);
-        assertEquals("Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})",
-                result);
-    }
-
-    @Test
-    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 void registerProvider(String key, Object impl, Class<?> interfaceClass) {
-        ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
-        repository.registerProvider(
-                key,
-                impl,
-                serviceDescriptor,
-                null,
-                null
-        );
-    }
-
-    private Channel getChannelInstance() {
-        return new Channel() {
-            private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
-
-            @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-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/ListTelnetHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/ListTelnetHandlerTest.java
deleted file mode 100644
index 8ff6220..0000000
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/ListTelnetHandlerTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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.qos.legacy;
-
-import org.apache.dubbo.common.utils.ReflectUtils;
-import org.apache.dubbo.config.ServiceConfigBase;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.legacy.service.DemoService;
-import org.apache.dubbo.qos.legacy.service.DemoServiceImpl;
-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.ServiceDescriptor;
-import org.apache.dubbo.rpc.model.ServiceRepository;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.mock;
-
-/**
- * CountTelnetHandlerTest.java
- */
-public class ListTelnetHandlerTest {
-
-    private static TelnetHandler list = new ListTelnetHandler();
-    private Channel mockChannel;
-    private final ServiceRepository repository = ApplicationModel.getServiceRepository();
-
-    @BeforeAll
-    public static void setUp() {
-        ProtocolUtils.closeAll();
-        DubboBootstrap.reset();
-    }
-
-    @AfterEach
-    public void after() {
-        ProtocolUtils.closeAll();
-        DubboBootstrap.reset();
-    }
-
-    @Test
-    public void testListDetailService() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName());
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String result = list.telnet(mockChannel, "-l DemoService");
-        for (Method method : DemoService.class.getMethods()) {
-            assertTrue(result.contains(ReflectUtils.getName(method)));
-        }
-    }
-
-    @Test
-    public void testListService() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName());
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String result = list.telnet(mockChannel, "DemoService");
-        for (Method method : DemoService.class.getMethods()) {
-            assertTrue(result.contains(method.getName()));
-        }
-    }
-
-    @Test
-    public void testList() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn(null);
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String result = list.telnet(mockChannel, "");
-        assertEquals("PROVIDER:\r\norg.apache.dubbo.qos.legacy.service.DemoService\r\n", result);
-    }
-
-    @Test
-    public void testListDetail() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn(null);
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String result = list.telnet(mockChannel, "-l");
-        assertEquals("PROVIDER:\r\norg.apache.dubbo.qos.legacy.service.DemoService ->  published: N\r\n", result);
-    }
-
-    @Test
-    public void testListDefault() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn(DemoService.class.getName());
-
-        registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class);
-
-        String result = list.telnet(mockChannel, "");
-        assertTrue(result.startsWith("Use default service org.apache.dubbo.qos.legacy.service.DemoService.\r\n" +
-                "org.apache.dubbo.qos.legacy.service.DemoService (as provider):\r\n"));
-        for (Method method : DemoService.class.getMethods()) {
-            assertTrue(result.contains(method.getName()));
-        }
-    }
-
-    @Test
-    public void testInvalidMessage() throws RemotingException {
-        mockChannel = mock(Channel.class);
-        given(mockChannel.getAttribute("telnet.service")).willReturn(null);
-
-        String result = list.telnet(mockChannel, "xx");
-        assertEquals("No such service: xx", result);
-    }
-
-    private void registerProvider(String key, Object impl, Class<?> interfaceClass) {
-        ServiceConfigBase sc = mock(ServiceConfigBase.class);
-        given(sc.getRegistries()).willReturn(new ArrayList<>());
-
-        ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
-        repository.registerProvider(
-                key,
-                impl,
-                serviceDescriptor,
-                sc,
-                null
-        );
-    }
-}
diff --git a/dubbo-remoting/dubbo-remoting-zookeeper/pom.xml b/dubbo-remoting/dubbo-remoting-zookeeper/pom.xml
index d97f848..ce375e4 100644
--- a/dubbo-remoting/dubbo-remoting-zookeeper/pom.xml
+++ b/dubbo-remoting/dubbo-remoting-zookeeper/pom.xml
@@ -47,6 +47,12 @@
         <dependency>
             <groupId>org.apache.zookeeper</groupId>
             <artifactId>zookeeper</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.apache.curator</groupId>