Skip to content

Commit f8806c8

Browse files
christophstroblThomas Darimont
authored and
Thomas Darimont
committed
DATAREDIS-285 - LettuceConnection.execute should fully read response.
'LettuceConnection' used 'ByteArrayOutput' as default for execute which did not fully read all values that might be included in the response. To overcome this issue 'TypeHints' have been added which map a given 'CommandType' to the most recent 'CommandOutput'. As not all commands could be mapped that way due to the result depending on input parameters in some cases, it might be required to manually provide the type hint when calling execute. Additional tests have been added to assert 'execute' works properly for 'jedis', 'jredis' and 'srp'. Original Pull Request: #49
1 parent 5ef23c2 commit f8806c8

File tree

6 files changed

+326
-8
lines changed

6 files changed

+326
-8
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ jredisVersion=06052013
44
jedisVersion=2.4.1
55
springVersion=3.2.8.RELEASE
66
log4jVersion=1.2.17
7-
version=1.2.0.RELEASE
7+
version=1.2.1.BUILD-SNAPSHOT
88
srpVersion=0.7
99
jacksonVersion=1.8.8
1010
fasterXmlJacksonVersion=2.2.0

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java

Lines changed: 238 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,22 @@
1717

1818
import static com.lambdaworks.redis.protocol.CommandType.*;
1919

20+
import java.lang.reflect.Constructor;
2021
import java.util.ArrayList;
2122
import java.util.Arrays;
2223
import java.util.Collections;
24+
import java.util.HashMap;
2325
import java.util.LinkedList;
2426
import java.util.List;
2527
import java.util.Map;
2628
import java.util.Properties;
2729
import java.util.Queue;
2830
import java.util.Set;
31+
import java.util.concurrent.ConcurrentHashMap;
2932
import java.util.concurrent.Future;
3033
import java.util.concurrent.TimeUnit;
3134

35+
import org.springframework.beans.BeanUtils;
3236
import org.springframework.core.convert.converter.Converter;
3337
import org.springframework.dao.DataAccessException;
3438
import org.springframework.dao.InvalidDataAccessApiUsageException;
@@ -46,7 +50,7 @@
4650
import org.springframework.data.redis.connection.convert.Converters;
4751
import org.springframework.data.redis.connection.convert.TransactionResultConverter;
4852
import org.springframework.util.Assert;
49-
import org.springframework.util.NumberUtils;
53+
import org.springframework.util.ClassUtils;
5054
import org.springframework.util.ObjectUtils;
5155

5256
import com.lambdaworks.redis.RedisAsyncConnection;
@@ -55,9 +59,22 @@
5559
import com.lambdaworks.redis.SortArgs;
5660
import com.lambdaworks.redis.ZStoreArgs;
5761
import com.lambdaworks.redis.codec.RedisCodec;
62+
import com.lambdaworks.redis.output.BooleanOutput;
5863
import com.lambdaworks.redis.output.ByteArrayOutput;
64+
import com.lambdaworks.redis.output.DateOutput;
65+
import com.lambdaworks.redis.output.DoubleOutput;
66+
import com.lambdaworks.redis.output.IntegerOutput;
67+
import com.lambdaworks.redis.output.KeyListOutput;
68+
import com.lambdaworks.redis.output.KeyValueOutput;
69+
import com.lambdaworks.redis.output.MapOutput;
70+
import com.lambdaworks.redis.output.MultiOutput;
71+
import com.lambdaworks.redis.output.StatusOutput;
72+
import com.lambdaworks.redis.output.ValueListOutput;
73+
import com.lambdaworks.redis.output.ValueOutput;
74+
import com.lambdaworks.redis.output.ValueSetOutput;
5975
import com.lambdaworks.redis.protocol.Command;
6076
import com.lambdaworks.redis.protocol.CommandArgs;
77+
import com.lambdaworks.redis.protocol.CommandOutput;
6178
import com.lambdaworks.redis.protocol.CommandType;
6279
import com.lambdaworks.redis.pubsub.RedisPubSubConnection;
6380

@@ -72,6 +89,7 @@
7289
public class LettuceConnection implements RedisConnection {
7390

7491
static final RedisCodec<byte[], byte[]> CODEC = new BytesRedisCodec();
92+
private static final TypeHints typeHints = new TypeHints();
7593

7694
private final com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> asyncSharedConn;
7795
private final com.lambdaworks.redis.RedisConnection<byte[], byte[]> sharedConn;
@@ -253,7 +271,23 @@ private Object await(Command cmd) {
253271
return getAsyncConnection().await(cmd, timeout, TimeUnit.MILLISECONDS);
254272
}
255273

274+
@Override
256275
public Object execute(String command, byte[]... args) {
276+
return execute(command, null, args);
277+
}
278+
279+
/**
280+
* 'Native' or 'raw' execution of the given command along-side the given arguments.
281+
*
282+
* @see RedisCommands#execute(String, byte[]...)
283+
* @param command Command to execute
284+
* @param commandOutputTypeHint Type of Output to use, may be (may be {@literal null}).
285+
* @param args Possible command arguments (may be {@literal null})
286+
* @return execution result.
287+
*/
288+
@SuppressWarnings({ "rawtypes", "unchecked" })
289+
public Object execute(String command, CommandOutput commandOutputTypeHint, byte[]... args) {
290+
257291
Assert.hasText(command, "a valid command needs to be specified");
258292
try {
259293
String name = command.trim().toUpperCase();
@@ -264,16 +298,15 @@ public Object execute(String command, byte[]... args) {
264298
cmdArg.addKeys(args);
265299
}
266300

301+
CommandOutput expectedOutput = commandOutputTypeHint != null ? commandOutputTypeHint : typeHints.getTypeHint(cmd);
267302
if (isPipelined()) {
268-
pipeline(new LettuceResult(getAsyncConnection().dispatch(cmd, new ByteArrayOutput<byte[], byte[]>(CODEC),
269-
cmdArg)));
303+
pipeline(new LettuceResult(getAsyncConnection().dispatch(cmd, expectedOutput, cmdArg)));
270304
return null;
271305
} else if (isQueueing()) {
272-
transaction(new LettuceResult(getAsyncConnection().dispatch(cmd, new ByteArrayOutput<byte[], byte[]>(CODEC),
273-
cmdArg)));
306+
transaction(new LettuceResult(getAsyncConnection().dispatch(cmd, expectedOutput, cmdArg)));
274307
return null;
275308
} else {
276-
return await(getAsyncConnection().dispatch(cmd, new ByteArrayOutput<byte[], byte[]>(CODEC), cmdArg));
309+
return await(getAsyncConnection().dispatch(cmd, expectedOutput, cmdArg));
277310
}
278311
} catch (RedisException ex) {
279312
throw convertLettuceAccessException(ex);
@@ -2776,7 +2809,7 @@ public Long time() {
27762809
* support the time command natively.
27772810
* see https://github.com/wg/lettuce/issues/19
27782811
*/
2779-
List<byte[]> result = (List<byte[]>)eval("return redis.call('TIME')".getBytes(),ReturnType.MULTI,0);
2812+
List<byte[]> result = (List<byte[]>) eval("return redis.call('TIME')".getBytes(), ReturnType.MULTI, 0);
27802813

27812814
Assert.notEmpty(result, "Received invalid result from server. Expected 2 items in collection.");
27822815
Assert.isTrue(result.size() == 2, "Received invalid nr of arguments from redis server. Expected 2 received "
@@ -2931,4 +2964,202 @@ private ZStoreArgs zStoreArgs(Aggregate aggregate, int[] weights) {
29312964
return args;
29322965
}
29332966

2967+
/**
2968+
* {@link TypeHints} provide {@link CommandOutput} information for a given {@link CommandType}.
2969+
*
2970+
* @since 1.2.1
2971+
*/
2972+
static class TypeHints {
2973+
2974+
@SuppressWarnings("rawtypes")//
2975+
private static final Map<CommandType, Class<? extends CommandOutput>> COMMAND_OUTPUT_TYPE_MAPPING = new HashMap<CommandType, Class<? extends CommandOutput>>();
2976+
2977+
@SuppressWarnings("rawtypes")//
2978+
private static final Map<Class<?>, Constructor<CommandOutput>> CONSTRUCTORS = new ConcurrentHashMap<Class<?>, Constructor<CommandOutput>>();
2979+
2980+
{
2981+
// INTEGER
2982+
COMMAND_OUTPUT_TYPE_MAPPING.put(BITCOUNT, IntegerOutput.class);
2983+
COMMAND_OUTPUT_TYPE_MAPPING.put(BITOP, IntegerOutput.class);
2984+
COMMAND_OUTPUT_TYPE_MAPPING.put(DBSIZE, IntegerOutput.class);
2985+
COMMAND_OUTPUT_TYPE_MAPPING.put(DECR, IntegerOutput.class);
2986+
COMMAND_OUTPUT_TYPE_MAPPING.put(DECRBY, IntegerOutput.class);
2987+
COMMAND_OUTPUT_TYPE_MAPPING.put(DEL, IntegerOutput.class);
2988+
COMMAND_OUTPUT_TYPE_MAPPING.put(GETBIT, IntegerOutput.class);
2989+
COMMAND_OUTPUT_TYPE_MAPPING.put(HDEL, IntegerOutput.class);
2990+
COMMAND_OUTPUT_TYPE_MAPPING.put(HINCRBY, IntegerOutput.class);
2991+
COMMAND_OUTPUT_TYPE_MAPPING.put(HLEN, IntegerOutput.class);
2992+
COMMAND_OUTPUT_TYPE_MAPPING.put(INCR, IntegerOutput.class);
2993+
COMMAND_OUTPUT_TYPE_MAPPING.put(INCRBY, IntegerOutput.class);
2994+
COMMAND_OUTPUT_TYPE_MAPPING.put(LINSERT, IntegerOutput.class);
2995+
COMMAND_OUTPUT_TYPE_MAPPING.put(LLEN, IntegerOutput.class);
2996+
COMMAND_OUTPUT_TYPE_MAPPING.put(LPUSH, IntegerOutput.class);
2997+
COMMAND_OUTPUT_TYPE_MAPPING.put(LPUSHX, IntegerOutput.class);
2998+
COMMAND_OUTPUT_TYPE_MAPPING.put(LREM, IntegerOutput.class);
2999+
COMMAND_OUTPUT_TYPE_MAPPING.put(PTTL, IntegerOutput.class);
3000+
COMMAND_OUTPUT_TYPE_MAPPING.put(PUBLISH, IntegerOutput.class);
3001+
COMMAND_OUTPUT_TYPE_MAPPING.put(RPUSH, IntegerOutput.class);
3002+
COMMAND_OUTPUT_TYPE_MAPPING.put(RPUSHX, IntegerOutput.class);
3003+
COMMAND_OUTPUT_TYPE_MAPPING.put(SADD, IntegerOutput.class);
3004+
COMMAND_OUTPUT_TYPE_MAPPING.put(SCARD, IntegerOutput.class);
3005+
COMMAND_OUTPUT_TYPE_MAPPING.put(SDIFFSTORE, IntegerOutput.class);
3006+
COMMAND_OUTPUT_TYPE_MAPPING.put(SETBIT, IntegerOutput.class);
3007+
COMMAND_OUTPUT_TYPE_MAPPING.put(SETRANGE, IntegerOutput.class);
3008+
COMMAND_OUTPUT_TYPE_MAPPING.put(SINTERSTORE, IntegerOutput.class);
3009+
COMMAND_OUTPUT_TYPE_MAPPING.put(SREM, IntegerOutput.class);
3010+
COMMAND_OUTPUT_TYPE_MAPPING.put(SUNIONSTORE, IntegerOutput.class);
3011+
COMMAND_OUTPUT_TYPE_MAPPING.put(STRLEN, IntegerOutput.class);
3012+
COMMAND_OUTPUT_TYPE_MAPPING.put(TTL, IntegerOutput.class);
3013+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZADD, IntegerOutput.class);
3014+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZCOUNT, IntegerOutput.class);
3015+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZINTERSTORE, IntegerOutput.class);
3016+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZRANK, IntegerOutput.class);
3017+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZREM, IntegerOutput.class);
3018+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZREMRANGEBYRANK, IntegerOutput.class);
3019+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZREMRANGEBYSCORE, IntegerOutput.class);
3020+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZREVRANK, IntegerOutput.class);
3021+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZUNIONSTORE, IntegerOutput.class);
3022+
3023+
// DOUBLE
3024+
COMMAND_OUTPUT_TYPE_MAPPING.put(HINCRBYFLOAT, DoubleOutput.class);
3025+
COMMAND_OUTPUT_TYPE_MAPPING.put(INCRBYFLOAT, DoubleOutput.class);
3026+
COMMAND_OUTPUT_TYPE_MAPPING.put(MGET, ValueListOutput.class);
3027+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZINCRBY, DoubleOutput.class);
3028+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZSCORE, DoubleOutput.class);
3029+
3030+
// MAP
3031+
COMMAND_OUTPUT_TYPE_MAPPING.put(HGETALL, MapOutput.class);
3032+
3033+
// KEY LIST
3034+
COMMAND_OUTPUT_TYPE_MAPPING.put(HKEYS, KeyListOutput.class);
3035+
COMMAND_OUTPUT_TYPE_MAPPING.put(KEYS, KeyListOutput.class);
3036+
3037+
// KEY VALUE
3038+
COMMAND_OUTPUT_TYPE_MAPPING.put(BRPOP, KeyValueOutput.class);
3039+
3040+
// SINGLE VALUE
3041+
COMMAND_OUTPUT_TYPE_MAPPING.put(BRPOPLPUSH, ValueOutput.class);
3042+
COMMAND_OUTPUT_TYPE_MAPPING.put(ECHO, ValueOutput.class);
3043+
COMMAND_OUTPUT_TYPE_MAPPING.put(GET, ValueOutput.class);
3044+
COMMAND_OUTPUT_TYPE_MAPPING.put(GETRANGE, ValueOutput.class);
3045+
COMMAND_OUTPUT_TYPE_MAPPING.put(GETSET, ValueOutput.class);
3046+
COMMAND_OUTPUT_TYPE_MAPPING.put(HGET, ValueOutput.class);
3047+
COMMAND_OUTPUT_TYPE_MAPPING.put(LINDEX, ValueOutput.class);
3048+
COMMAND_OUTPUT_TYPE_MAPPING.put(LPOP, ValueOutput.class);
3049+
COMMAND_OUTPUT_TYPE_MAPPING.put(RANDOMKEY, ValueOutput.class);
3050+
COMMAND_OUTPUT_TYPE_MAPPING.put(RENAME, ValueOutput.class);
3051+
COMMAND_OUTPUT_TYPE_MAPPING.put(RPOP, ValueOutput.class);
3052+
COMMAND_OUTPUT_TYPE_MAPPING.put(RPOPLPUSH, ValueOutput.class);
3053+
COMMAND_OUTPUT_TYPE_MAPPING.put(SPOP, ValueOutput.class);
3054+
COMMAND_OUTPUT_TYPE_MAPPING.put(SRANDMEMBER, ValueOutput.class);
3055+
3056+
// STATUS VALUE
3057+
COMMAND_OUTPUT_TYPE_MAPPING.put(BGREWRITEAOF, StatusOutput.class);
3058+
COMMAND_OUTPUT_TYPE_MAPPING.put(BGSAVE, StatusOutput.class);
3059+
COMMAND_OUTPUT_TYPE_MAPPING.put(CLIENT, StatusOutput.class);
3060+
COMMAND_OUTPUT_TYPE_MAPPING.put(DEBUG, StatusOutput.class);
3061+
COMMAND_OUTPUT_TYPE_MAPPING.put(DISCARD, StatusOutput.class);
3062+
COMMAND_OUTPUT_TYPE_MAPPING.put(FLUSHALL, StatusOutput.class);
3063+
COMMAND_OUTPUT_TYPE_MAPPING.put(FLUSHDB, StatusOutput.class);
3064+
COMMAND_OUTPUT_TYPE_MAPPING.put(HMSET, StatusOutput.class);
3065+
COMMAND_OUTPUT_TYPE_MAPPING.put(INFO, StatusOutput.class);
3066+
COMMAND_OUTPUT_TYPE_MAPPING.put(LSET, StatusOutput.class);
3067+
COMMAND_OUTPUT_TYPE_MAPPING.put(LTRIM, StatusOutput.class);
3068+
COMMAND_OUTPUT_TYPE_MAPPING.put(MIGRATE, StatusOutput.class);
3069+
COMMAND_OUTPUT_TYPE_MAPPING.put(MSET, StatusOutput.class);
3070+
COMMAND_OUTPUT_TYPE_MAPPING.put(QUIT, StatusOutput.class);
3071+
COMMAND_OUTPUT_TYPE_MAPPING.put(RESTORE, StatusOutput.class);
3072+
COMMAND_OUTPUT_TYPE_MAPPING.put(SAVE, StatusOutput.class);
3073+
COMMAND_OUTPUT_TYPE_MAPPING.put(SELECT, StatusOutput.class);
3074+
COMMAND_OUTPUT_TYPE_MAPPING.put(SET, StatusOutput.class);
3075+
COMMAND_OUTPUT_TYPE_MAPPING.put(SETEX, StatusOutput.class);
3076+
COMMAND_OUTPUT_TYPE_MAPPING.put(SHUTDOWN, StatusOutput.class);
3077+
COMMAND_OUTPUT_TYPE_MAPPING.put(SLAVEOF, StatusOutput.class);
3078+
COMMAND_OUTPUT_TYPE_MAPPING.put(SYNC, StatusOutput.class);
3079+
COMMAND_OUTPUT_TYPE_MAPPING.put(TYPE, StatusOutput.class);
3080+
COMMAND_OUTPUT_TYPE_MAPPING.put(WATCH, StatusOutput.class);
3081+
COMMAND_OUTPUT_TYPE_MAPPING.put(UNWATCH, StatusOutput.class);
3082+
3083+
// VALUE LIST
3084+
COMMAND_OUTPUT_TYPE_MAPPING.put(HMGET, ValueListOutput.class);
3085+
COMMAND_OUTPUT_TYPE_MAPPING.put(MGET, ValueListOutput.class);
3086+
COMMAND_OUTPUT_TYPE_MAPPING.put(HVALS, ValueListOutput.class);
3087+
COMMAND_OUTPUT_TYPE_MAPPING.put(LRANGE, ValueListOutput.class);
3088+
COMMAND_OUTPUT_TYPE_MAPPING.put(SORT, ValueListOutput.class);
3089+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZRANGE, ValueListOutput.class);
3090+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZRANGEBYSCORE, ValueListOutput.class);
3091+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZREVRANGE, ValueListOutput.class);
3092+
COMMAND_OUTPUT_TYPE_MAPPING.put(ZREVRANGEBYSCORE, ValueListOutput.class);
3093+
3094+
// BOOLEAN
3095+
COMMAND_OUTPUT_TYPE_MAPPING.put(EXISTS, BooleanOutput.class);
3096+
COMMAND_OUTPUT_TYPE_MAPPING.put(EXPIRE, BooleanOutput.class);
3097+
COMMAND_OUTPUT_TYPE_MAPPING.put(EXPIREAT, BooleanOutput.class);
3098+
COMMAND_OUTPUT_TYPE_MAPPING.put(HEXISTS, BooleanOutput.class);
3099+
COMMAND_OUTPUT_TYPE_MAPPING.put(HSET, BooleanOutput.class);
3100+
COMMAND_OUTPUT_TYPE_MAPPING.put(HSETNX, BooleanOutput.class);
3101+
COMMAND_OUTPUT_TYPE_MAPPING.put(MOVE, BooleanOutput.class);
3102+
COMMAND_OUTPUT_TYPE_MAPPING.put(MSETNX, BooleanOutput.class);
3103+
COMMAND_OUTPUT_TYPE_MAPPING.put(PERSIST, BooleanOutput.class);
3104+
COMMAND_OUTPUT_TYPE_MAPPING.put(PEXPIRE, BooleanOutput.class);
3105+
COMMAND_OUTPUT_TYPE_MAPPING.put(PEXPIREAT, BooleanOutput.class);
3106+
COMMAND_OUTPUT_TYPE_MAPPING.put(RENAMENX, BooleanOutput.class);
3107+
COMMAND_OUTPUT_TYPE_MAPPING.put(SETNX, BooleanOutput.class);
3108+
COMMAND_OUTPUT_TYPE_MAPPING.put(SISMEMBER, BooleanOutput.class);
3109+
COMMAND_OUTPUT_TYPE_MAPPING.put(SMOVE, BooleanOutput.class);
3110+
3111+
// MULTI
3112+
COMMAND_OUTPUT_TYPE_MAPPING.put(EXEC, MultiOutput.class);
3113+
COMMAND_OUTPUT_TYPE_MAPPING.put(MULTI, MultiOutput.class);
3114+
3115+
// DATE
3116+
COMMAND_OUTPUT_TYPE_MAPPING.put(LASTSAVE, DateOutput.class);
3117+
3118+
// VALUE SET
3119+
COMMAND_OUTPUT_TYPE_MAPPING.put(SDIFF, ValueSetOutput.class);
3120+
COMMAND_OUTPUT_TYPE_MAPPING.put(SINTER, ValueSetOutput.class);
3121+
COMMAND_OUTPUT_TYPE_MAPPING.put(SMEMBERS, ValueSetOutput.class);
3122+
COMMAND_OUTPUT_TYPE_MAPPING.put(SUNION, ValueSetOutput.class);
3123+
}
3124+
3125+
/**
3126+
* Returns the {@link CommandOutput} mapped for given {@link CommandType} or {@link ByteArrayOutput} as default.
3127+
*
3128+
* @param type
3129+
* @return {@link ByteArrayOutput} as default when no matching {@link CommandOutput} available.
3130+
*/
3131+
@SuppressWarnings("rawtypes")
3132+
public CommandOutput getTypeHint(CommandType type) {
3133+
return getTypeHint(type, new ByteArrayOutput<byte[], byte[]>(CODEC));
3134+
}
3135+
3136+
/**
3137+
* Returns the {@link CommandOutput} mapped for given {@link CommandType} given {@link CommandOutput} as default.
3138+
*
3139+
* @param type
3140+
* @return
3141+
*/
3142+
@SuppressWarnings("rawtypes")
3143+
public CommandOutput getTypeHint(CommandType type, CommandOutput defaultType) {
3144+
3145+
if (type == null || !COMMAND_OUTPUT_TYPE_MAPPING.containsKey(type)) {
3146+
return defaultType;
3147+
}
3148+
CommandOutput<?, ?, ?> outputType = instanciateCommandOutput(COMMAND_OUTPUT_TYPE_MAPPING.get(type));
3149+
return outputType != null ? outputType : defaultType;
3150+
}
3151+
3152+
@SuppressWarnings({ "rawtypes", "unchecked" })
3153+
private CommandOutput<?, ?, ?> instanciateCommandOutput(Class<? extends CommandOutput> type) {
3154+
3155+
Assert.notNull(type, "Cannot create instance for 'null' type.");
3156+
Constructor<CommandOutput> constructor = CONSTRUCTORS.get(type);
3157+
if (constructor == null) {
3158+
constructor = (Constructor<CommandOutput>) ClassUtils.getConstructorIfAvailable(type, RedisCodec.class);
3159+
CONSTRUCTORS.put(type, constructor);
3160+
}
3161+
return BeanUtils.instantiateClass(constructor, CODEC);
3162+
}
3163+
}
3164+
29343165
}

src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionIntegrationTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919
import static org.junit.Assert.*;
2020

2121
import java.util.HashSet;
22+
import java.util.List;
2223
import java.util.Set;
2324
import java.util.concurrent.BlockingDeque;
2425
import java.util.concurrent.LinkedBlockingDeque;
2526
import java.util.concurrent.TimeUnit;
2627

28+
import org.hamcrest.core.AllOf;
29+
import org.hamcrest.core.IsInstanceOf;
2730
import org.junit.After;
2831
import org.junit.Test;
32+
import org.junit.internal.matchers.IsCollectionContaining;
2933
import org.junit.runner.RunWith;
3034
import org.springframework.dao.InvalidDataAccessApiUsageException;
3135
import org.springframework.data.redis.SettingsUtils;
@@ -313,4 +317,20 @@ public void testPoolNPE() {
313317
factory2.getConnection().dbSize();
314318
factory2.destroy();
315319
}
320+
321+
/**
322+
* @see DATAREDIS-285
323+
*/
324+
@SuppressWarnings("unchecked")
325+
@Test
326+
public void testExecuteShouldConvertArrayReplyCorrectly() {
327+
connection.set("spring", "awesome");
328+
connection.set("data", "cool");
329+
connection.set("redis", "supercalifragilisticexpialidocious");
330+
331+
assertThat(
332+
connection.execute("MGET", "spring".getBytes(), "data".getBytes(), "redis".getBytes()),
333+
AllOf.allOf(IsInstanceOf.instanceOf(List.class), IsCollectionContaining.hasItems("awesome".getBytes(),
334+
"cool".getBytes(), "supercalifragilisticexpialidocious".getBytes())));
335+
}
316336
}

0 commit comments

Comments
 (0)