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

nizhikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 3fd73a97572 IGNITE-18533 Management API to kill client connection 
(#10478)
3fd73a97572 is described below

commit 3fd73a97572136ca7d0a50e3e50472560287e1af
Author: Nikolay <[email protected]>
AuthorDate: Sat Jan 14 17:37:55 2023 +0300

    IGNITE-18533 Management API to kill client connection (#10478)
---
 docs/_docs/sql-reference/operational-commands.adoc | 27 +++++++
 .../internal/commandline/query/KillCommand.java    | 29 +++++++
 .../internal/commandline/query/KillSubcommand.java |  5 ++
 .../testsuites/IgniteControlUtilityTestSuite.java  |  4 +-
 ...dShTest.java => KillCommandsControlShTest.java} | 33 +++++++-
 .../processors/odbc/ClientListenerProcessor.java   | 17 +++-
 .../ignite/internal/sql/SqlCommandProcessor.java   | 16 ++++
 .../org/apache/ignite/internal/sql/SqlKeyword.java |  3 +
 .../org/apache/ignite/internal/sql/SqlParser.java  |  5 ++
 .../internal/sql/command/SqlKillClientCommand.java | 65 ++++++++++++++++
 .../client/VisorClientConnectionDropTask.java      | 90 ++++++++++++++++++++++
 ...ridCommandHandlerClusterByClassTest_help.output |  7 ++
 ...andHandlerClusterByClassWithSSLTest_help.output |  7 ++
 .../internal/processors/query/h2/QueryParser.java  |  2 +-
 .../apache/ignite/util/KillCommandsMXBeanTest.java | 22 ++++++
 .../apache/ignite/util/KillCommandsSQLTest.java    | 15 ++++
 .../org/apache/ignite/util/KillCommandsTests.java  | 68 ++++++++++++++++
 17 files changed, 407 insertions(+), 8 deletions(-)

diff --git a/docs/_docs/sql-reference/operational-commands.adoc 
b/docs/_docs/sql-reference/operational-commands.adoc
index 308bcf6ca9f..875fc30431b 100644
--- a/docs/_docs/sql-reference/operational-commands.adoc
+++ b/docs/_docs/sql-reference/operational-commands.adoc
@@ -397,3 +397,30 @@ control.bat --kill CONSISTENCY
 ----
 
 --
+== KILL CLIENT operations
+
+The `KILL CLIENT` command allows you to cancel local client (thin/jdbc/odbc) 
connection.
+
+[tabs]
+--
+
+tab:Unix[]
+[source,bash]
+----
+./control.sh --kill CLIENT connection_id [--node-id node_id]
+----
+
+tab:Windows[]
+[source,bash]
+----
+control.bat --kill CLIENT connection_id [--node-id node_id]
+----
+
+--
+
+=== Parameters
+
+* `connection_id` - corresponds to the connection id of the client. Specify 
ALL to drop all connections. Note, connection_id are local value.
+Value differs on nodes for the same client. You can always find parameter 
values with the 
link:monitoring-metrics/system-views#client_connections[CLIENT_CONNECTIONS] 
view.
+
+* `node_id` - corresponds to the node_id to drop connection from.
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/query/KillCommand.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/query/KillCommand.java
index b906eae5b50..d13d4e98a15 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/query/KillCommand.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/query/KillCommand.java
@@ -28,6 +28,7 @@ import org.apache.ignite.internal.commandline.Command;
 import org.apache.ignite.internal.commandline.CommandArgIterator;
 import org.apache.ignite.internal.commandline.CommandLogger;
 import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.visor.client.VisorClientConnectionDropTask;
 import org.apache.ignite.internal.visor.compute.VisorComputeCancelSessionTask;
 import 
org.apache.ignite.internal.visor.compute.VisorComputeCancelSessionTaskArg;
 import org.apache.ignite.internal.visor.consistency.VisorConsistencyCancelTask;
@@ -53,8 +54,10 @@ import org.apache.ignite.mxbean.TransactionsMXBean;
 import static java.util.Collections.singletonMap;
 import static 
org.apache.ignite.internal.QueryMXBeanImpl.EXPECTED_GLOBAL_QRY_ID_FORMAT;
 import static org.apache.ignite.internal.commandline.CommandList.KILL;
+import static org.apache.ignite.internal.commandline.CommandLogger.optional;
 import static 
org.apache.ignite.internal.commandline.TaskExecutor.BROADCAST_UUID;
 import static 
org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode;
+import static 
org.apache.ignite.internal.commandline.query.KillSubcommand.CLIENT;
 import static 
org.apache.ignite.internal.commandline.query.KillSubcommand.COMPUTE;
 import static 
org.apache.ignite.internal.commandline.query.KillSubcommand.CONTINUOUS;
 import static org.apache.ignite.internal.commandline.query.KillSubcommand.SCAN;
@@ -212,6 +215,23 @@ public class KillCommand extends AbstractCommand<Object> {
 
                 break;
 
+            case CLIENT:
+                taskName = VisorClientConnectionDropTask.class.getName();
+
+                String argVal = argIter.nextArg("connection id");
+
+                taskArgs = "ALL".equals(argVal) ? null : 
Long.parseLong(argVal);
+
+                if ("--node-id".equals(argIter.peekNextArg())) {
+                    argIter.nextArg("--node-id");
+
+                    nodeId = UUID.fromString(argIter.nextArg("node_id"));
+                }
+                else
+                    nodeId = BROADCAST_UUID;
+
+                break;
+
             default:
                 throw new IllegalArgumentException("Unknown kill subcommand: " 
+ cmd);
         }
@@ -247,6 +267,15 @@ public class KillCommand extends AbstractCommand<Object> {
         usage(log, "Kill continuous query by routine id:", KILL, params, 
CONTINUOUS.toString(),
             "origin_node_id", "routine_id");
 
+        params.clear();
+
+        params.put("connection_id", "Connection identifier or ALL.");
+        params.put("--node-id node_id", "Node id to drop connection from.");
+
+        usage(log, "Kill client connection by id:", KILL, params, 
CLIENT.toString(),
+            "connection_id",
+            optional("--node-id node_id"));
+
         usage(log, "Kill running snapshot by snapshot name:", KILL, 
singletonMap("snapshot_name", "Snapshot name."),
             SNAPSHOT.toString(), "snapshot_name");
     }
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/query/KillSubcommand.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/query/KillSubcommand.java
index 2c70166cdcf..0a2d08e658c 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/query/KillSubcommand.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/query/KillSubcommand.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.commandline.query;
 
+import org.apache.ignite.mxbean.ClientProcessorMXBean;
 import org.apache.ignite.mxbean.ComputeMXBean;
 import org.apache.ignite.mxbean.QueryMXBean;
 import org.apache.ignite.mxbean.ServiceMXBean;
@@ -32,6 +33,7 @@ import org.apache.ignite.mxbean.TransactionsMXBean;
  * @see TransactionsMXBean
  * @see ServiceMXBean
  * @see SnapshotMXBean
+ * @see ClientProcessorMXBean
  */
 public enum KillSubcommand {
     /** Kill compute task. */
@@ -57,4 +59,7 @@ public enum KillSubcommand {
 
     /** Kill consistency tasks. */
     CONSISTENCY,
+
+    /** Kill client connection. */
+    CLIENT
 }
diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite.java
index cc94b30326b..4e89a330601 100644
--- 
a/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite.java
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite.java
@@ -36,7 +36,7 @@ import org.apache.ignite.util.GridCommandHandlerMetadataTest;
 import org.apache.ignite.util.GridCommandHandlerSslTest;
 import org.apache.ignite.util.GridCommandHandlerTest;
 import org.apache.ignite.util.GridCommandHandlerWithSSLTest;
-import org.apache.ignite.util.KillCommandsCommandShTest;
+import org.apache.ignite.util.KillCommandsControlShTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 
@@ -65,7 +65,7 @@ import org.junit.runners.Suite;
     GridCommandHandlerInterruptCommandTest.class,
     GridCommandHandlerMetadataTest.class,
 
-    KillCommandsCommandShTest.class,
+    KillCommandsControlShTest.class,
 
     BaselineEventsLocalTest.class,
     BaselineEventsRemoteTest.class,
diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsCommandShTest.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsControlShTest.java
similarity index 90%
rename from 
modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsCommandShTest.java
rename to 
modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsControlShTest.java
index 13157af7758..412ed3b6ed1 100644
--- 
a/modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsCommandShTest.java
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsControlShTest.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.util;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
@@ -43,6 +42,8 @@ import org.apache.ignite.spi.systemview.view.SystemView;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.junit.Test;
 
+import static java.util.Arrays.asList;
+import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_INVALID_ARGUMENTS;
 import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
 import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_UNEXPECTED_ERROR;
 import static 
org.apache.ignite.internal.processors.cache.persistence.snapshot.AbstractSnapshotSelfTest.doSnapshotCancellationTest;
@@ -52,6 +53,7 @@ import static 
org.apache.ignite.testframework.GridTestUtils.assertContains;
 import static org.apache.ignite.testframework.GridTestUtils.assertNotContains;
 import static org.apache.ignite.util.KillCommandsTests.PAGES_CNT;
 import static org.apache.ignite.util.KillCommandsTests.PAGE_SZ;
+import static 
org.apache.ignite.util.KillCommandsTests.doTestCancelClientConnection;
 import static org.apache.ignite.util.KillCommandsTests.doTestCancelComputeTask;
 import static 
org.apache.ignite.util.KillCommandsTests.doTestCancelContinuousQuery;
 import static org.apache.ignite.util.KillCommandsTests.doTestCancelSQLQuery;
@@ -60,7 +62,7 @@ import static 
org.apache.ignite.util.KillCommandsTests.doTestCancelTx;
 import static org.apache.ignite.util.KillCommandsTests.doTestScanQueryCancel;
 
 /** Tests cancel of user created entities via control.sh. */
-public class KillCommandsCommandShTest extends 
GridCommandHandlerClusterByClassAbstractTest {
+public class KillCommandsControlShTest extends 
GridCommandHandlerClusterByClassAbstractTest {
     /** */
     private static List<IgniteEx> srvs;
 
@@ -153,6 +155,31 @@ public class KillCommandsCommandShTest extends 
GridCommandHandlerClusterByClassA
         });
     }
 
+    /** @throws Exception If failed. */
+    @Test
+    public void testCancelClientConnection() {
+        doTestCancelClientConnection(srvs, (nodeId, connId) -> {
+            List<String> params = new ArrayList<>(
+                asList("--kill", "client", connId == null ? "ALL" : 
Long.toString(connId))
+            );
+
+            if (nodeId != null)
+                params.addAll(asList("--node-id", nodeId.toString()));
+
+            assertEquals(EXIT_CODE_OK, execute(params));
+        });
+    }
+
+    /** @throws Exception If failed. */
+    @Test
+    public void testCancelClientConnectionWrongParams() {
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--kill", "client"));
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--kill", "client", 
"not_a_number"));
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--kill", "client", 
"1", "--node-id"));
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--kill", "client", 
"1", "--node-id", "not_an_uuid"));
+        assertEquals("Unknown connection id", EXIT_CODE_UNEXPECTED_ERROR, 
execute("--kill", "client", "123"));
+    }
+
     /** */
     @Test
     public void testCancelSnapshot() {
@@ -344,7 +371,7 @@ public class KillCommandsCommandShTest extends 
GridCommandHandlerClusterByClassA
 
         injectTestSystemOut();
 
-        List<String> cmd = new ArrayList<>(Arrays.asList(
+        List<String> cmd = new ArrayList<>(asList(
             "--consistency", "repair",
             ConsistencyCommand.STRATEGY, 
ReadRepairStrategy.CHECK_ONLY.toString(),
             ConsistencyCommand.PARTITIONS, "0",
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
index 74e1cfb0de2..28b70f97cf2 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
@@ -52,6 +52,7 @@ import 
org.apache.ignite.internal.util.nio.ssl.GridNioSslFilter;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.mxbean.ClientProcessorMXBean;
+import org.apache.ignite.plugin.security.SecurityPermission;
 import org.apache.ignite.spi.IgnitePortProtocol;
 import org.apache.ignite.spi.systemview.view.ClientConnectionView;
 import org.jetbrains.annotations.NotNull;
@@ -566,10 +567,17 @@ public class ClientListenerProcessor extends 
GridProcessorAdapter {
             
ctx.config().getClientConnectorConfiguration().getThinClientConfiguration().sendServerExceptionStackTraceToClient()
 : send;
     }
 
+    /**
+     * @return MX bean instance.
+     */
+    public ClientProcessorMXBean mxBean() {
+        return new ClientProcessorMXBeanImpl();
+    }
+
     /**
      * ClientProcessorMXBean interface.
      */
-    private class ClientProcessorMXBeanImpl implements ClientProcessorMXBean {
+    public class ClientProcessorMXBeanImpl implements ClientProcessorMXBean {
         /** {@inheritDoc} */
         @Override public List<String> getConnections() {
             Collection<? extends GridNioSession> sessions = srv.sessions();
@@ -592,12 +600,17 @@ public class ClientListenerProcessor extends 
GridProcessorAdapter {
 
         /** {@inheritDoc} */
         @Override public void dropAllConnections() {
+            ctx.security().authorize(null, SecurityPermission.ADMIN_OPS);
+
             closeAllSessions();
         }
 
         /** {@inheritDoc} */
         @Override public boolean dropConnection(long id) {
-            assert (id >> 32) == ctx.discovery().localNode().order() : 
"Invalid connection id.";
+            ctx.security().authorize(null, SecurityPermission.ADMIN_OPS);
+
+            if ((id >> 32) != ctx.discovery().localNode().order())
+                return false;
 
             Collection<? extends GridNioSession> sessions = srv.sessions();
 
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlCommandProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlCommandProcessor.java
index c6f28765310..3a1ff15b40a 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlCommandProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlCommandProcessor.java
@@ -55,6 +55,7 @@ import 
org.apache.ignite.internal.sql.command.SqlDropIndexCommand;
 import org.apache.ignite.internal.sql.command.SqlDropStatisticsCommand;
 import org.apache.ignite.internal.sql.command.SqlDropUserCommand;
 import org.apache.ignite.internal.sql.command.SqlIndexColumn;
+import org.apache.ignite.internal.sql.command.SqlKillClientCommand;
 import org.apache.ignite.internal.sql.command.SqlKillComputeTaskCommand;
 import org.apache.ignite.internal.sql.command.SqlKillContinuousQueryCommand;
 import org.apache.ignite.internal.sql.command.SqlKillQueryCommand;
@@ -105,6 +106,8 @@ public class SqlCommandProcessor {
 
         if (isDdl(cmdNative))
             runCommandNativeDdl(cmdNative);
+        else if (cmdNative instanceof SqlKillClientCommand)
+            processKillClientCommand((SqlKillClientCommand)cmdNative);
         else if (cmdNative instanceof SqlKillComputeTaskCommand)
             
processKillComputeTaskCommand((SqlKillComputeTaskCommand)cmdNative);
         else if (cmdNative instanceof SqlKillTransactionCommand)
@@ -137,6 +140,7 @@ public class SqlCommandProcessor {
             || cmd instanceof SqlCreateUserCommand
             || cmd instanceof SqlAlterUserCommand
             || cmd instanceof SqlDropUserCommand
+            || cmd instanceof SqlKillClientCommand
             || cmd instanceof SqlKillComputeTaskCommand
             || cmd instanceof SqlKillServiceCommand
             || cmd instanceof SqlKillTransactionCommand
@@ -178,6 +182,18 @@ public class SqlCommandProcessor {
             .cancelScan(cmd.getOriginNodeId(), cmd.getCacheName(), 
cmd.getQryId());
     }
 
+    /**
+     * Process kill client command.
+     *
+     * @param cmd Command.
+     */
+    private void processKillClientCommand(SqlKillClientCommand cmd) {
+        if (cmd.connectionId() == null)
+            ctx.sqlListener().mxBean().dropAllConnections();
+        else
+            ctx.sqlListener().mxBean().dropConnection(cmd.connectionId());
+    }
+
     /**
      * Process kill compute task command.
      *
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlKeyword.java 
b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlKeyword.java
index 4b001b661fd..54f52535bae 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlKeyword.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlKeyword.java
@@ -44,6 +44,9 @@ public class SqlKeyword {
     /** Keyword: SERVICE. */
     public static final String SERVICE = "SERVICE";
 
+    /** Keyword: CLIENT. */
+    public static final String CLIENT = "CLIENT";
+
     /** Keyword: ALTER. */
     public static final String ALTER = "ALTER";
 
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParser.java 
b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParser.java
index d97e0f56f9b..024fbd9d957 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParser.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParser.java
@@ -30,6 +30,7 @@ import 
org.apache.ignite.internal.sql.command.SqlCreateUserCommand;
 import org.apache.ignite.internal.sql.command.SqlDropIndexCommand;
 import org.apache.ignite.internal.sql.command.SqlDropStatisticsCommand;
 import org.apache.ignite.internal.sql.command.SqlDropUserCommand;
+import org.apache.ignite.internal.sql.command.SqlKillClientCommand;
 import org.apache.ignite.internal.sql.command.SqlKillComputeTaskCommand;
 import org.apache.ignite.internal.sql.command.SqlKillContinuousQueryCommand;
 import org.apache.ignite.internal.sql.command.SqlKillQueryCommand;
@@ -44,6 +45,7 @@ import org.jetbrains.annotations.Nullable;
 import static org.apache.ignite.internal.sql.SqlKeyword.ALTER;
 import static org.apache.ignite.internal.sql.SqlKeyword.ANALYZE;
 import static org.apache.ignite.internal.sql.SqlKeyword.BEGIN;
+import static org.apache.ignite.internal.sql.SqlKeyword.CLIENT;
 import static org.apache.ignite.internal.sql.SqlKeyword.COMMIT;
 import static org.apache.ignite.internal.sql.SqlKeyword.COMPUTE;
 import static org.apache.ignite.internal.sql.SqlKeyword.CONTINUOUS;
@@ -331,6 +333,9 @@ public class SqlParser {
 
                 case TRANSACTION:
                     return new SqlKillTransactionCommand().parse(lex);
+
+                case CLIENT:
+                    return new SqlKillClientCommand().parse(lex);
             }
         }
 
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlKillClientCommand.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlKillClientCommand.java
new file mode 100644
index 00000000000..40f2c7c4837
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlKillClientCommand.java
@@ -0,0 +1,65 @@
+/*
+ * 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.ignite.internal.sql.command;
+
+import org.apache.ignite.internal.sql.SqlLexer;
+import org.apache.ignite.internal.sql.SqlLexerTokenType;
+import org.apache.ignite.internal.sql.SqlParserUtils;
+import org.apache.ignite.spi.systemview.view.ClientConnectionView;
+
+/**
+ * KILL CLIENT command.
+ *
+ * @see org.apache.ignite.internal.visor.client.VisorClientConnectionDropTask
+ * @see ClientConnectionView#connectionId()
+ */
+public class SqlKillClientCommand implements SqlCommand {
+    /** Connections id. */
+    private Long connectionId;
+
+    /** {@inheritDoc} */
+    @Override public SqlCommand parse(SqlLexer lex) {
+        if (lex.shift()) {
+            if (lex.tokenType() == SqlLexerTokenType.DEFAULT) {
+                String connIdStr = lex.token();
+
+                if (!"ALL".equals(connIdStr))
+                    connectionId = Long.parseLong(connIdStr);
+
+                return this;
+            }
+        }
+
+        throw SqlParserUtils.error(lex, "Expected client connection id or 
ALL.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public String schemaName() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void schemaName(String schemaName) {
+        // No-op.
+    }
+
+    /** @return Connection id to drop. */
+    public Long connectionId() {
+        return connectionId;
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/client/VisorClientConnectionDropTask.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/client/VisorClientConnectionDropTask.java
new file mode 100644
index 00000000000..b5a0d949e4e
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/client/VisorClientConnectionDropTask.java
@@ -0,0 +1,90 @@
+/*
+ * 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.ignite.internal.visor.client;
+
+import java.util.List;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.processors.task.GridVisorManagementTask;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.VisorMultiNodeTask;
+import org.apache.ignite.mxbean.ClientProcessorMXBean;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Task to cancel client connection(s).
+ */
+@GridInternal
+@GridVisorManagementTask
+public class VisorClientConnectionDropTask extends VisorMultiNodeTask<Long, 
Void, Boolean> {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** {@inheritDoc} */
+    @Override protected VisorJob<Long, Boolean> job(Long arg) {
+        return new VisorClientConnectionDropJob(arg, debug);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Void reduce0(List<ComputeJobResult> results) throws 
IgniteException {
+        boolean res = false;
+
+        for (ComputeJobResult jobRes : results) {
+            if (jobRes.getException() != null)
+                throw jobRes.getException();
+
+            res |= jobRes.<Boolean>getData();
+        }
+
+        if (!res)
+            throw new IgniteException("No connection was dropped");
+
+        return null;
+    }
+
+    /**
+     * Job to cancel client connection(s).
+     */
+    private static class VisorClientConnectionDropJob extends VisorJob<Long, 
Boolean> {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /**
+         * Create job with specified argument.
+         *
+         * @param arg Job argument.
+         * @param debug Flag indicating whether debug information should be 
printed into node log.
+         */
+        protected VisorClientConnectionDropJob(@Nullable Long arg, boolean 
debug) {
+            super(arg, debug);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected Boolean run(@Nullable Long arg) throws 
IgniteException {
+            ClientProcessorMXBean bean = 
ignite.context().sqlListener().mxBean();
+
+            if (arg != null)
+                return bean.dropConnection(arg);
+
+            bean.dropAllConnections();
+
+            return true;
+        }
+    }
+}
diff --git 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
index 2b9eba75a01..52882c92e8f 100644
--- 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
+++ 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
@@ -133,6 +133,13 @@ This utility can do the following commands:
       routine_id      - Routine identifier.
       origin_node_id  - Originating node id.
 
+  Kill client connection by id:
+    control.(sh|bat) --kill CLIENT connection_id [--node-id node_id]
+
+    Parameters:
+      connection_id      - Connection identifier or ALL.
+      --node-id node_id  - Node id to drop connection from.
+
   Kill running snapshot by snapshot name:
     control.(sh|bat) --kill SNAPSHOT snapshot_name
 
diff --git 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
index 2b9eba75a01..52882c92e8f 100644
--- 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
+++ 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
@@ -133,6 +133,13 @@ This utility can do the following commands:
       routine_id      - Routine identifier.
       origin_node_id  - Originating node id.
 
+  Kill client connection by id:
+    control.(sh|bat) --kill CLIENT connection_id [--node-id node_id]
+
+    Parameters:
+      connection_id      - Connection identifier or ALL.
+      --node-id node_id  - Node id to drop connection from.
+
   Kill running snapshot by snapshot name:
     control.(sh|bat) --kill SNAPSHOT snapshot_name
 
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
index 3b952e2066c..952e33c58bf 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
@@ -83,7 +83,7 @@ public class QueryParser {
     private static final Pattern INTERNAL_CMD_RE = Pattern.compile(
         
"^(create|drop)\\s+index|^analyze\\s|^refresh\\sstatistics|^drop\\sstatistics|^alter\\s+table|^copy"
 +
             "|^set|^begin|^commit|^rollback|^(create|alter|drop)\\s+user" +
-            
"|^kill\\s+(query|scan|continuous|compute|service|transaction)|show|help|grant|revoke",
+            
"|^kill\\s+(query|scan|continuous|compute|service|transaction|client)|show|help|grant|revoke",
         Pattern.CASE_INSENSITIVE);
 
     /** Indexing. */
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsMXBeanTest.java
 
b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsMXBeanTest.java
index c8f828bb201..38bf9f5beb1 100644
--- 
a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsMXBeanTest.java
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsMXBeanTest.java
@@ -32,7 +32,10 @@ import org.apache.ignite.internal.QueryMXBeanImpl;
 import org.apache.ignite.internal.ServiceMXBeanImpl;
 import org.apache.ignite.internal.TransactionsMXBeanImpl;
 import 
org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotMXBeanImpl;
+import org.apache.ignite.internal.processors.odbc.ClientListenerProcessor;
+import org.apache.ignite.internal.util.typedef.G;
 import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.mxbean.ClientProcessorMXBean;
 import org.apache.ignite.mxbean.ComputeMXBean;
 import org.apache.ignite.mxbean.QueryMXBean;
 import org.apache.ignite.mxbean.ServiceMXBean;
@@ -46,6 +49,7 @@ import static org.apache.ignite.cluster.ClusterState.ACTIVE;
 import static 
org.apache.ignite.internal.processors.cache.persistence.snapshot.AbstractSnapshotSelfTest.doSnapshotCancellationTest;
 import static org.apache.ignite.util.KillCommandsTests.PAGES_CNT;
 import static org.apache.ignite.util.KillCommandsTests.PAGE_SZ;
+import static 
org.apache.ignite.util.KillCommandsTests.doTestCancelClientConnection;
 import static org.apache.ignite.util.KillCommandsTests.doTestCancelComputeTask;
 import static 
org.apache.ignite.util.KillCommandsTests.doTestCancelContinuousQuery;
 import static org.apache.ignite.util.KillCommandsTests.doTestCancelSQLQuery;
@@ -188,6 +192,24 @@ public class KillCommandsMXBeanTest extends 
GridCommonAbstractTest {
             snpName -> snpMxBean.cancelSnapshot(snpName));
     }
 
+    /** */
+    @Test
+    public void testCancelClientConnection() {
+        doTestCancelClientConnection(srvs, (nodeId, connId) -> {
+            ClientProcessorMXBean cliMxBean = getMxBean(
+                (nodeId == null ? srvs.get(1) : G.ignite(nodeId)).name(),
+                "Clients",
+                ClientListenerProcessor.class.getSimpleName(),
+                ClientProcessorMXBean.class
+            );
+
+            if (connId == null)
+                cliMxBean.dropAllConnections();
+            else
+                cliMxBean.dropConnection(connId);
+        } );
+    }
+
     /** */
     @Test
     public void testCancelUnknownSnapshot() {
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsSQLTest.java
 
b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsSQLTest.java
index 65163ddaae1..1458565e372 100644
--- 
a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsSQLTest.java
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsSQLTest.java
@@ -26,12 +26,14 @@ import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.util.typedef.G;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Test;
 
 import static org.apache.ignite.cluster.ClusterState.ACTIVE;
 import static 
org.apache.ignite.internal.processors.cache.index.AbstractSchemaSelfTest.queryProcessor;
+import static org.apache.ignite.internal.sql.SqlKeyword.CLIENT;
 import static org.apache.ignite.internal.sql.SqlKeyword.COMPUTE;
 import static org.apache.ignite.internal.sql.SqlKeyword.CONTINUOUS;
 import static org.apache.ignite.internal.sql.SqlKeyword.KILL;
@@ -42,6 +44,7 @@ import static 
org.apache.ignite.internal.sql.SqlKeyword.TRANSACTION;
 import static 
org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
 import static org.apache.ignite.util.KillCommandsTests.PAGES_CNT;
 import static org.apache.ignite.util.KillCommandsTests.PAGE_SZ;
+import static 
org.apache.ignite.util.KillCommandsTests.doTestCancelClientConnection;
 import static org.apache.ignite.util.KillCommandsTests.doTestCancelComputeTask;
 import static 
org.apache.ignite.util.KillCommandsTests.doTestCancelContinuousQuery;
 import static org.apache.ignite.util.KillCommandsTests.doTestCancelSQLQuery;
@@ -72,6 +75,9 @@ public class KillCommandsSQLTest extends 
GridCommonAbstractTest {
     /** */
     public static final String KILL_CQ_QRY = KILL + " " + CONTINUOUS;
 
+    /** */
+    public static final String KILL_CLI_QRY = KILL + " " + CLIENT;
+
     /** */
     private static List<IgniteEx> srvs;
 
@@ -147,6 +153,15 @@ public class KillCommandsSQLTest extends 
GridCommonAbstractTest {
             execute(killCli, KILL_CQ_QRY + " '" + nodeId.toString() + "'" + " 
'" + routineId.toString() + "'"));
     }
 
+    /** */
+    @Test
+    public void testCancelClientConnection() {
+        doTestCancelClientConnection(srvs, (nodeId, connId) -> execute(
+            nodeId == null ? srvs.get(1) : G.ignite(nodeId),
+            KILL_CLI_QRY + " " + (connId == null ? "ALL" : 
Long.toString(connId))
+        ));
+    }
+
     /** */
     @Test
     public void testCancelUnknownScanQuery() {
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsTests.java 
b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsTests.java
index 56fec69fcc1..99ed62fdae9 100644
--- 
a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsTests.java
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsTests.java
@@ -28,16 +28,22 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import javax.cache.Cache;
 import javax.cache.CacheException;
 import javax.cache.event.CacheEntryEvent;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.Ignition;
 import org.apache.ignite.cache.query.ContinuousQuery;
 import org.apache.ignite.cache.query.QueryCursor;
 import org.apache.ignite.cache.query.ScanQuery;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.client.ClientConnectionException;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.cluster.ClusterState;
+import org.apache.ignite.configuration.ClientConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
@@ -55,6 +61,7 @@ import org.apache.ignite.transactions.Transaction;
 
 import static 
org.apache.ignite.internal.managers.systemview.ScanQuerySystemView.SCAN_QRY_SYS_VIEW;
 import static 
org.apache.ignite.internal.processors.cache.index.AbstractSchemaSelfTest.queryProcessor;
+import static 
org.apache.ignite.internal.processors.odbc.ClientListenerProcessor.CLIENT_LISTENER_PORT;
 import static 
org.apache.ignite.internal.processors.service.IgniteServiceProcessor.SVCS_VIEW;
 import static 
org.apache.ignite.testframework.GridTestUtils.assertThrowsAnyCause;
 import static 
org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
@@ -472,6 +479,67 @@ class KillCommandsTests {
         }
     }
 
+    /**
+     * Test cancel of the client connection(s).
+     *
+     * @param srvs Server nodes.
+     * @param cliCanceler Client connection cancel closure.
+     */
+    public static void doTestCancelClientConnection(List<IgniteEx> srvs, 
BiConsumer<UUID, Long> cliCanceler) {
+        ClientConfiguration cfg = new ClientConfiguration()
+            .setAddresses("127.0.0.1:" + 
srvs.get(0).localNode().attribute(CLIENT_LISTENER_PORT))
+            .setPartitionAwarenessEnabled(false);
+
+        IgniteClient cli0 = Ignition.startClient(cfg);
+        IgniteClient cli1 = Ignition.startClient(cfg);
+        IgniteClient cli2 = Ignition.startClient(cfg);
+        IgniteClient cli3 = Ignition.startClient(new ClientConfiguration()
+            .setAddresses("127.0.0.1:" + 
srvs.get(1).localNode().attribute(CLIENT_LISTENER_PORT))
+            .setPartitionAwarenessEnabled(false));
+
+        assertEquals(ClusterState.ACTIVE, cli0.cluster().state());
+        assertEquals(ClusterState.ACTIVE, cli1.cluster().state());
+        assertEquals(ClusterState.ACTIVE, cli2.cluster().state());
+        assertEquals(ClusterState.ACTIVE, cli3.cluster().state());
+
+        List<List<?>> conns = execute(srvs.get(0), "SELECT CONNECTION_ID FROM 
SYS.CLIENT_CONNECTIONS ORDER BY 1");
+
+        cliCanceler.accept(srvs.get(0).localNode().id(), 
(Long)conns.get(0).get(0));
+
+        Predicate<IgniteClient> checker = cli -> {
+            try {
+                return waitForCondition(() -> {
+                    try {
+                        cli.cluster().state();
+
+                        return false;
+                    }
+                    catch (ClientConnectionException e) {
+                        return true;
+                    }
+                }, 10_000);
+            }
+            catch (Exception e) {
+                return false;
+            }
+        };
+
+        assertTrue(checker.test(cli0));
+        assertEquals(ClusterState.ACTIVE, cli1.cluster().state());
+        assertEquals(ClusterState.ACTIVE, cli2.cluster().state());
+        assertEquals(ClusterState.ACTIVE, cli3.cluster().state());
+
+        cliCanceler.accept(srvs.get(0).localNode().id(), null);
+
+        assertTrue(checker.test(cli1));
+        assertTrue(checker.test(cli2));
+        assertEquals(ClusterState.ACTIVE, cli3.cluster().state());
+
+        cliCanceler.accept(null, null);
+
+        assertTrue(checker.test(cli3));
+    }
+
     /** */
     public interface TestService extends Service {
         /** */


Reply via email to