diff --git a/pom.xml b/pom.xml
index 617c18c957..d15db14de6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.dataspring-data-redis
- 2.1.0.BUILD-SNAPSHOT
+ 2.1.0.DATAREDIS-698-SNAPSHOTSpring Data Redis
diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
index aede7d0b95..e87dbd62cc 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
@@ -15,17 +15,8 @@
*/
package org.springframework.data.redis.connection;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.Queue;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
@@ -3047,7 +3038,7 @@ public void openPipeline() {
*/
@Override
public Object execute(String command) {
- return execute(command, (byte[][]) null);
+ return execute(command, EMPTY_2D_BYTE_ARRAY);
}
/*
@@ -3201,6 +3192,16 @@ public Cursor> hScan(byte[] key, ScanOptions options) {
return this.delegate.hScan(key, options);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisSetCommands#hStrLen(byte[], byte[])
+ */
+ @Nullable
+ @Override
+ public Long hStrLen(byte[] key, byte[] field) {
+ return convertAndReturn(delegate.hStrLen(key, field), identityConverter);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisServerCommands#setClientName(java.lang.String)
@@ -3263,6 +3264,15 @@ public String setValue(String value) {
});
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#hStrLen(java.lang.String, java.lang.String)
+ */
+ @Override
+ public Long hStrLen(String key, String field) {
+ return hStrLen(serialize(key), serialize(field));
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#sScan(java.lang.String, org.springframework.data.redis.core.ScanOptions)
@@ -3472,8 +3482,8 @@ private T convertAndReturn(@Nullable Object value, Converter converter) {
return null;
}
-
- return value == null ? null : ObjectUtils.nullSafeEquals(converter, identityConverter) ? (T) value : (T) converter.convert(value);
+ return value == null ? null
+ : ObjectUtils.nullSafeEquals(converter, identityConverter) ? (T) value : (T) converter.convert(value);
}
private void addResultConverter(Converter, ?> converter) {
diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisClusterConnection.java
index 9fbefc4282..7b06aebd25 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisClusterConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisClusterConnection.java
@@ -15,10 +15,13 @@
*/
package org.springframework.data.redis.connection;
+import java.util.Collection;
import java.util.List;
import java.util.Properties;
import org.springframework.data.redis.core.types.RedisClientInfo;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
/**
* @author Christoph Strobl
@@ -131,4 +134,29 @@ default Long time(RedisClusterNode node) {
default List getClientList(RedisClusterNode node) {
return serverCommands().getClientList(node);
}
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisClusterConnection#execute(String, byte[], Collection)
+ */
+ @Nullable
+ @Override
+ @SuppressWarnings("unchecked")
+ default T execute(String command, byte[] key, Collection args) {
+
+ Assert.notNull(command, "Command must not be null!");
+ Assert.notNull(key, "Key must not be null!");
+ Assert.notNull(args, "Args must not be null!");
+
+ byte[][] commandArgs = new byte[args.size() + 1][];
+
+ commandArgs[0] = key;
+ int targetIndex = 1;
+
+ for (byte[] binaryArgument : args) {
+ commandArgs[targetIndex++] = binaryArgument;
+ }
+
+ return (T) execute(command, commandArgs);
+ }
}
diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
index 62591279bf..5ee509042c 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
@@ -916,6 +916,13 @@ default Cursor> hScan(byte[] key, ScanOptions options) {
return hashCommands().hScan(key, options);
}
+ /** @deprecated in favor of {@link RedisConnection#hashCommands()}. */
+ @Override
+ @Deprecated
+ default Long hStrLen(byte[] key, byte[] field) {
+ return hashCommands().hStrLen(key, field);
+ }
+
// GEO COMMANDS
/** @deprecated in favor of {@link RedisConnection#geoCommands()}}. */
diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java
index 03979020d4..a737afd0b7 100644
--- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java
@@ -578,4 +578,82 @@ default Flux> hGetAll(ByteBuffer key) {
* @see Redis Documentation: HGETALL
*/
Flux>>> hGetAll(Publisher commands);
+
+ /**
+ * @author Christoph Strobl
+ * @see Redis Documentation: HSTRLEN
+ * @since 2.1
+ */
+ class HStrLenCommand extends KeyCommand {
+
+ private ByteBuffer field;
+
+ /**
+ * Creates a new {@link HStrLenCommand} given a {@code key}.
+ *
+ * @param key can be {@literal null}.
+ * @param field must not be {@literal null}.
+ */
+ private HStrLenCommand(@Nullable ByteBuffer key, ByteBuffer field) {
+
+ super(key);
+ this.field = field;
+ }
+
+ /**
+ * Specify the {@code field} within the hash to get the length of the {@code value} of.ΓΈ
+ *
+ * @param field must not be {@literal null}.
+ * @return new instance of {@link HStrLenCommand}.
+ */
+ public static HStrLenCommand lengthOf(ByteBuffer field) {
+
+ Assert.notNull(field, "Field must not be null!");
+ return new HStrLenCommand(null, field);
+ }
+
+ /**
+ * Define the {@code key} the hash is stored at.
+ *
+ * @param key must not be {@literal null}.
+ * @return new instance of {@link HStrLenCommand}.
+ */
+ public HStrLenCommand from(ByteBuffer key) {
+ return new HStrLenCommand(key, field);
+ }
+
+ /**
+ * @return the field.
+ */
+ public ByteBuffer getField() {
+ return field;
+ }
+ }
+
+ /**
+ * Get the length of the value associated with {@code field}. If either the {@code key} or the {@code field} do not
+ * exist, {@code 0} is emitted.
+ *
+ * @param key must not be {@literal null}.
+ * @param field must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 2.1
+ */
+ default Mono hStrLen(ByteBuffer key, ByteBuffer field) {
+
+ Assert.notNull(key, "Key must not be null!");
+ Assert.notNull(field, "Field must not be null!");
+
+ return hStrLen(Mono.just(HStrLenCommand.lengthOf(field).from(key))).next().map(NumericResponse::getOutput);
+ }
+
+ /**
+ * Get the length of the value associated with {@code field}. If either the {@code key} or the {@code field} do not
+ * exist, {@code 0} is emitted.
+ *
+ * @param commands must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 2.1
+ */
+ Flux> hStrLen(Publisher commands);
}
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterConnection.java
index 7af3f274e8..b690ac475a 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisClusterConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterConnection.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection;
+import java.util.Collection;
import java.util.Set;
import org.springframework.lang.Nullable;
@@ -56,6 +57,27 @@ public interface RedisClusterConnection extends RedisConnection, RedisClusterCom
@Nullable
byte[] randomKey(RedisClusterNode node);
+ /**
+ * Execute the given command for the {@code key} provided potentially appending args.
+ * This method, other than {@link #execute(String, byte[]...)}, dispatches the command to the {@code key} serving
+ * master node.
+ *
+ *
+ *
+ * // SET foo bar EX 10 NX
+ * execute("SET", "foo".getBytes(), asBinaryList("bar", "EX", 10, "NX"))
+ *
+ *
+ *
+ * @param command must not be {@literal null}.
+ * @param key must not be {@literal null}.
+ * @param args must not be {@literal null}.
+ * @return command result as delivered by the underlying Redis driver. Can be {@literal null}.
+ * @since 2.1
+ */
+ @Nullable
+ T execute(String command, byte[] key, Collection args);
+
/**
* Get {@link RedisClusterServerCommands}.
*
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java
index d4bb421b6f..7da765a3d1 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java
@@ -183,4 +183,16 @@ public interface RedisHashCommands {
* @see Redis Documentation: HSCAN
*/
Cursor> hScan(byte[] key, ScanOptions options);
+
+ /**
+ * Returns the length of the value associated with {@code field} in the hash stored at {@code key}. If the key or the
+ * field do not exist, {@code 0} is returned.
+ *
+ * @param key must not be {@literal null}.
+ * @param field must not be {@literal null}.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.1
+ */
+ @Nullable
+ Long hStrLen(byte[] key, byte[] field);
}
diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
index 86e24b8c15..1b8ad7eacb 100644
--- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
@@ -1509,6 +1509,18 @@ interface StringTuple extends Tuple {
*/
Cursor> hScan(String key, ScanOptions options);
+ /**
+ * Returns the length of the value associated with {@code field} in the hash stored at {@code key}. If the key or the
+ * field do not exist, {@code 0} is returned.
+ *
+ * @param key must not be {@literal null}.
+ * @param field must not be {@literal null}.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.1
+ */
+ @Nullable
+ Long hStrLen(String key, String field);
+
// -------------------------------------------------------------------------
// Methods dealing with HyperLogLog
// -------------------------------------------------------------------------
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientUtils.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientUtils.java
new file mode 100644
index 0000000000..3fa9537b93
--- /dev/null
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientUtils.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed 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.springframework.data.redis.connection.jedis;
+
+import redis.clients.jedis.BinaryJedis;
+import redis.clients.jedis.Builder;
+import redis.clients.jedis.Client;
+import redis.clients.jedis.Connection;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.Protocol;
+import redis.clients.jedis.Protocol.Command;
+import redis.clients.jedis.Queable;
+import redis.clients.jedis.Response;
+import redis.clients.util.RedisOutputStream;
+import redis.clients.util.SafeEncoder;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.DirectFieldAccessor;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * Utility class to dispatch arbitrary Redis commands using Jedis commands.
+ *
+ * @author Christoph Strobl
+ * @author Mark Paluch
+ * @since 2.1
+ */
+@SuppressWarnings({ "unchecked", "ConstantConditions" })
+class JedisClientUtils {
+
+ private static final Field CLIENT_FIELD;
+ private static final Method SEND_COMMAND;
+ private static final Method GET_RESPONSE;
+ private static final Method PROTOCOL_SEND_COMMAND;
+ private static final Set KNOWN_COMMANDS;
+ private static final Builder