diff --git a/modules/clients/pom.xml b/modules/clients/pom.xml
index 578c898d9c3e3..649e79c28e915 100644
--- a/modules/clients/pom.xml
+++ b/modules/clients/pom.xml
@@ -50,7 +50,7 @@
redis.clients
jedis
- 2.9.0
+ 8.0.1
test
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolConnectSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolConnectSelfTest.java
index b22f823042fb6..16c0e76ee8030 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolConnectSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolConnectSelfTest.java
@@ -18,9 +18,11 @@
package org.apache.ignite.internal.processors.rest.protocols.tcp.redis;
import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.ignite.testframework.GridTestUtils;
import org.junit.Assert;
import org.junit.Test;
import redis.clients.jedis.Jedis;
+import redis.clients.jedis.exceptions.JedisDataException;
import static org.apache.ignite.internal.util.IgniteUtils.KB;
@@ -28,31 +30,25 @@
* Tests for Connection commands of Redis protocol.
*/
public class RedisProtocolConnectSelfTest extends RedisCommonAbstractTest {
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testPing() throws Exception {
+ public void testPing() {
try (Jedis jedis = pool.getResource()) {
Assert.assertEquals("PONG", jedis.ping());
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testEcho() throws Exception {
+ public void testEcho() {
try (Jedis jedis = pool.getResource()) {
Assert.assertEquals("Hello, grid!", jedis.echo("Hello, grid!"));
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testSelect() throws Exception {
+ public void testSelect() {
try (Jedis jedis = pool.getResource()) {
// connected to cache with index 0
jedis.set("k0", "v0");
@@ -78,6 +74,36 @@ public void testSelect() throws Exception {
}
}
+ /** */
+ @Test
+ public void testClient() {
+ try (Jedis jedis = pool.getResource()) {
+ Assert.assertNull(jedis.clientGetname());
+
+ Assert.assertEquals("OK", jedis.clientSetname("test-client"));
+ Assert.assertEquals("test-client", jedis.clientGetname());
+
+ // The name is connection-scoped.
+ try (Jedis jedis2 = pool.getResource()) {
+ Assert.assertNull(jedis2.clientGetname());
+ }
+
+ Assert.assertEquals("test-client", jedis.clientGetname());
+ }
+ }
+
+ /** */
+ @Test
+ public void testClientUnknownSubcommand() {
+ try (Jedis jedis = pool.getResource()) {
+ GridTestUtils.assertThrows(log, () -> jedis.clientUnpause(), JedisDataException.class,
+ "Unknown subcommand 'UNPAUSE' for 'client' command");
+
+ // The connection is still usable.
+ Assert.assertEquals("PONG", jedis.ping());
+ }
+ }
+
/** */
@Test
public void testSetGetLongString() {
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolStringSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolStringSelfTest.java
index c1d5e08416fa6..90f22820a59cd 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolStringSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolStringSelfTest.java
@@ -26,16 +26,15 @@
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisDataException;
+import redis.clients.jedis.params.SetParams;
/**
* Tests for String commands of Redis protocol.
*/
public class RedisProtocolStringSelfTest extends RedisCommonAbstractTest {
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testGet() throws Exception {
+ public void testGet() {
try (Jedis jedis = pool.getResource()) {
jcache().put("getKey1", "getVal1");
@@ -55,11 +54,9 @@ public void testGet() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testGetSet() throws Exception {
+ public void testGetSet() {
try (Jedis jedis = pool.getResource()) {
jcache().put("getSetKey1", "1");
@@ -79,11 +76,9 @@ public void testGetSet() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testMGet() throws Exception {
+ public void testMGet() {
try (Jedis jedis = pool.getResource()) {
jcache().put("getKey1", "getVal1");
jcache().put("getKey2", 0);
@@ -99,19 +94,15 @@ public void testMGet() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testMGetDirectOrder() throws Exception {
+ public void testMGetDirectOrder() {
testMGetOrder(true);
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testMGetReverseOrder() throws Exception {
+ public void testMGetReverseOrder() {
testMGetOrder(false);
}
@@ -153,11 +144,9 @@ public void testMGetOrder(boolean directOrder) {
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testMGetDuplicates() throws Exception {
+ public void testMGetDuplicates() {
try (Jedis jedis = pool.getResource()) {
jcache().put("key-A", "value-A");
jcache().put("key-B", "value-B");
@@ -188,14 +177,14 @@ public void testSet() throws Exception {
Assert.assertEquals("b0", jcache().get("setKey2"));
// test options.
- jedis.set("setKey1", "2", "nx");
- jedis.set("setKey3", "3", "nx", "px", EXPIRE_MS);
+ jedis.set("setKey1", "2", SetParams.setParams().nx());
+ jedis.set("setKey3", "3", SetParams.setParams().nx().px(EXPIRE_MS));
Assert.assertEquals("1", jcache().get("setKey1"));
Assert.assertEquals("3", jcache().get("setKey3"));
- jedis.set("setKey1", "2", "xx", "ex", EXPIRE_SEC);
- jedis.set("setKey4", "4", "xx");
+ jedis.set("setKey1", "2", SetParams.setParams().xx().ex(EXPIRE_SEC));
+ jedis.set("setKey4", "4", SetParams.setParams().xx());
Assert.assertEquals("2", jcache().get("setKey1"));
Assert.assertNull(jcache().get("setKey4"));
@@ -208,11 +197,9 @@ public void testSet() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testMSet() throws Exception {
+ public void testMSet() {
try (Jedis jedis = pool.getResource()) {
jedis.mset("setKey1", "1", "setKey2", "2");
@@ -221,11 +208,9 @@ public void testMSet() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testIncrDecr() throws Exception {
+ public void testIncrDecr() {
try (Jedis jedis = pool.getResource()) {
Assert.assertEquals(1, (long)jedis.incr("newKeyIncr"));
Assert.assertEquals(-1, (long)jedis.decr("newKeyDecr"));
@@ -305,11 +290,9 @@ public void testIncrDecr() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testIncrDecrBy() throws Exception {
+ public void testIncrDecrBy() {
try (Jedis jedis = pool.getResource()) {
Assert.assertEquals(2, (long)jedis.incrBy("newKeyIncrBy", 2));
Assert.assertEquals(-2, (long)jedis.decrBy("newKeyDecrBy", 2));
@@ -362,11 +345,9 @@ public void testIncrDecrBy() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testAppend() throws Exception {
+ public void testAppend() {
try (Jedis jedis = pool.getResource()) {
Assert.assertEquals(5, (long)jedis.append("appendKey1", "Hello"));
Assert.assertEquals(12, (long)jedis.append("appendKey1", " World!"));
@@ -384,11 +365,9 @@ public void testAppend() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testStrlen() throws Exception {
+ public void testStrlen() {
try (Jedis jedis = pool.getResource()) {
Assert.assertEquals(0, (long)jedis.strlen("strlenKeyNonExisting"));
@@ -409,11 +388,9 @@ public void testStrlen() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testSetRange() throws Exception {
+ public void testSetRange() {
try (Jedis jedis = pool.getResource()) {
Assert.assertEquals(0, (long)jedis.setrange("setRangeKey1", 0, ""));
@@ -458,11 +435,9 @@ public void testSetRange() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testGetRange() throws Exception {
+ public void testGetRange() {
try (Jedis jedis = pool.getResource()) {
Assert.assertEquals("", jedis.getrange("getRangeKeyNonExisting", 0, 0));
@@ -486,11 +461,9 @@ public void testGetRange() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testDel() throws Exception {
+ public void testDel() {
jcache().put("delKey1", "abc");
jcache().put("delKey2", "abcd");
try (Jedis jedis = pool.getResource()) {
@@ -500,11 +473,9 @@ public void testDel() throws Exception {
}
}
- /**
- * @throws Exception If failed.
- */
+ /** */
@Test
- public void testExists() throws Exception {
+ public void testExists() {
jcache().put("existsKey1", "abc");
jcache().put("existsKey2", "abcd");
try (Jedis jedis = pool.getResource()) {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java
index 368a2c3b767db..032026e04ee59 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java
@@ -223,7 +223,10 @@ public enum GridRestCommand {
WARM_UP("warmup"),
/** probe. */
- PROBE("probe");
+ PROBE("probe"),
+
+ /** Client info. */
+ CLIENT("client");
/** Enum values. */
private static final GridRestCommand[] VALS = values();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java
index e417ede2f6e99..f7bd2dbf30c13 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java
@@ -57,6 +57,7 @@
import org.apache.ignite.internal.processors.rest.handlers.memory.MemoryMetricsCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.probe.GridProbeCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.query.QueryCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.server.GridClientInfoCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.task.GridTaskCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.top.GridTopologyCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.user.UserActionCommandHandler;
@@ -557,6 +558,7 @@ public GridRestProcessor(GridKernalContext ctx) {
addHandler(new MemoryMetricsCommandHandler(ctx));
addHandler(new NodeStateBeforeStartCommandHandler(ctx));
addHandler(new GridProbeCommandHandler(ctx));
+ addHandler(new GridClientInfoCommandHandler(ctx));
// Start protocols.
startTcpProtocol();
@@ -955,6 +957,7 @@ private void authorize(GridRestRequest req) throws SecurityException {
case REMOVE_USER:
case UPDATE_USER:
case PROBE:
+ case CLIENT:
break;
default:
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/server/GridRedisClientCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/server/GridRedisClientCommandHandler.java
new file mode 100644
index 0000000000000..e9b14dd5b6256
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/server/GridRedisClientCommandHandler.java
@@ -0,0 +1,107 @@
+/*
+ * 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.processors.rest.handlers.redis.server;
+
+import java.util.Collection;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.rest.handlers.redis.GridRedisCommandHandler;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisProtocolParser;
+import org.apache.ignite.internal.util.future.GridFinishedFuture;
+import org.apache.ignite.internal.util.nio.GridNioSession;
+import org.apache.ignite.internal.util.nio.GridNioSessionMetaKey;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+import static org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand.CLIENT;
+
+/**
+ * Redis CLIENT command handler.
+ *
+ * CLIENT is a connection-scoped command container, so it is handled locally, without a REST round trip.
+ * Only the subcommands that carry no cluster-wide state are supported, the rest are answered with an error.
+ */
+public class GridRedisClientCommandHandler implements GridRedisCommandHandler {
+ /** Supported commands. */
+ private static final Collection SUPPORTED_COMMANDS = U.sealList(CLIENT);
+
+ /** Session metadata key for the name set by CLIENT SETNAME. */
+ private static final int CLIENT_NAME_META_KEY = GridNioSessionMetaKey.nextUniqueKey();
+
+ /** Position of the first argument of a CLIENT subcommand. */
+ private static final int ARG_POS = 2;
+
+ /** {@inheritDoc} */
+ @Override public Collection supportedCommands() {
+ return SUPPORTED_COMMANDS;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture handleAsync(GridNioSession ses, GridRedisMessage msg) {
+ assert msg != null;
+
+ String subCmd = msg.key();
+
+ if (F.isEmpty(subCmd)) {
+ msg.setResponse(GridRedisProtocolParser.toGenericError(
+ "wrong number of arguments for 'client' command"));
+
+ return new GridFinishedFuture<>(msg);
+ }
+
+ switch (subCmd.toUpperCase()) {
+ case "SETNAME": {
+ String name = msg.aux(ARG_POS);
+
+ if (name == null || name.indexOf(' ') >= 0 || name.indexOf('\n') >= 0)
+ msg.setResponse(GridRedisProtocolParser.toGenericError(
+ "Client names cannot contain spaces, newlines or special characters."));
+ else {
+ ses.addMeta(CLIENT_NAME_META_KEY, name);
+
+ msg.setResponse(GridRedisProtocolParser.oKString());
+ }
+
+ break;
+ }
+
+ case "GETNAME": {
+ String name = ses.meta(CLIENT_NAME_META_KEY);
+
+ msg.setResponse(name == null
+ ? GridRedisProtocolParser.nil()
+ : GridRedisProtocolParser.toBulkString(name));
+
+ break;
+ }
+
+ case "SETINFO":
+ // The library name and version announced by a driver: accepted and ignored.
+ msg.setResponse(GridRedisProtocolParser.oKString());
+
+ break;
+
+ default:
+ msg.setResponse(GridRedisProtocolParser.toGenericError(
+ "Unknown subcommand '" + subCmd + "' for 'client' command"));
+ }
+
+ return new GridFinishedFuture<>(msg);
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/server/GridClientInfoCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/server/GridClientInfoCommandHandler.java
new file mode 100644
index 0000000000000..bd7ef382bb968
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/server/GridClientInfoCommandHandler.java
@@ -0,0 +1,61 @@
+/*
+ * 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.processors.rest.handlers.server;
+
+import java.util.Collection;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.rest.GridRestCommand;
+import org.apache.ignite.internal.processors.rest.GridRestResponse;
+import org.apache.ignite.internal.processors.rest.handlers.GridRestCommandHandlerAdapter;
+import org.apache.ignite.internal.processors.rest.request.GridRestRequest;
+import org.apache.ignite.internal.util.future.GridFinishedFuture;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+import static org.apache.ignite.internal.processors.rest.GridRestCommand.CLIENT;
+
+/**
+ * Command handler for CLIENT command (returns client info).
+ */
+public class GridClientInfoCommandHandler extends GridRestCommandHandlerAdapter {
+ /** Supported commands. */
+ private static final Collection SUPPORTED_COMMANDS = U.sealList(CLIENT);
+
+ /**
+ * @param ctx Context.
+ */
+ public GridClientInfoCommandHandler(GridKernalContext ctx) {
+ super(ctx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Collection supportedCommands() {
+ return SUPPORTED_COMMANDS;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture handleAsync(GridRestRequest req) {
+ assert CLIENT == req.command();
+
+ GridRestResponse res = new GridRestResponse();
+
+ res.setResponse(0);
+
+ return new GridFinishedFuture<>(res);
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java
index bc32fb49ee4ae..e9c44dd00b7f6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java
@@ -78,7 +78,9 @@ public enum GridRedisCommand {
/** FLUSHDB. */
FLUSHDB("FLUSHDB"),
/** FLUSHALL. */
- FLUSHALL("FLUSHALL");
+ FLUSHALL("FLUSHALL"),
+ /** CLIENT. */
+ CLIENT("CLIENT");
/** String for command. */
private final String cmd;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java
index 955eed9f2c8f8..4b6337955ba0c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java
@@ -29,6 +29,7 @@
import org.apache.ignite.internal.processors.rest.handlers.redis.key.GridRedisDelCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.redis.key.GridRedisExistsCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.redis.key.GridRedisExpireCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.server.GridRedisClientCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.redis.server.GridRedisDbSizeCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.redis.server.GridRedisFlushCommandHandler;
import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisAppendCommandHandler;
@@ -92,6 +93,7 @@ public GridRedisNioListener(IgniteLogger log, GridRestProtocolHandler hnd, GridK
// server commands.
addCommandHandler(new GridRedisDbSizeCommandHandler(log, hnd, ctx));
addCommandHandler(new GridRedisFlushCommandHandler(log, hnd, ctx));
+ addCommandHandler(new GridRedisClientCommandHandler());
}
/**