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 {
/** */