Skip to content

Commit d207a16

Browse files
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 ce222f7 commit d207a16

File tree

5 files changed

+315
-5
lines changed

5 files changed

+315
-5
lines changed

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

Lines changed: 228 additions & 5 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,6 +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;
53+
import org.springframework.util.ClassUtils;
4954
import org.springframework.util.ObjectUtils;
5055

5156
import com.lambdaworks.redis.RedisAsyncConnection;
@@ -54,9 +59,22 @@
5459
import com.lambdaworks.redis.SortArgs;
5560
import com.lambdaworks.redis.ZStoreArgs;
5661
import com.lambdaworks.redis.codec.RedisCodec;
62+
import com.lambdaworks.redis.output.BooleanOutput;
5763
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;
5875
import com.lambdaworks.redis.protocol.Command;
5976
import com.lambdaworks.redis.protocol.CommandArgs;
77+
import com.lambdaworks.redis.protocol.CommandOutput;
6078
import com.lambdaworks.redis.protocol.CommandType;
6179
import com.lambdaworks.redis.pubsub.RedisPubSubConnection;
6280

@@ -71,6 +89,7 @@
7189
public class LettuceConnection implements RedisConnection {
7290

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

7594
private final com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> asyncSharedConn;
7695
private final com.lambdaworks.redis.RedisConnection<byte[], byte[]> sharedConn;
@@ -252,7 +271,14 @@ private Object await(Command cmd) {
252271
return getAsyncConnection().await(cmd, timeout, TimeUnit.MILLISECONDS);
253272
}
254273

274+
@Override
255275
public Object execute(String command, byte[]... args) {
276+
return execute(command, null, args);
277+
}
278+
279+
@SuppressWarnings({ "rawtypes", "unchecked" })
280+
public Object execute(String command, CommandOutput commandOutputTypeHint, byte[]... args) {
281+
256282
Assert.hasText(command, "a valid command needs to be specified");
257283
try {
258284
String name = command.trim().toUpperCase();
@@ -263,16 +289,15 @@ public Object execute(String command, byte[]... args) {
263289
cmdArg.addKeys(args);
264290
}
265291

292+
CommandOutput expectedOutput = commandOutputTypeHint != null ? commandOutputTypeHint : typeHints.getTypeHint(cmd);
266293
if (isPipelined()) {
267-
pipeline(new LettuceResult(getAsyncConnection().dispatch(cmd, new ByteArrayOutput<byte[], byte[]>(CODEC),
268-
cmdArg)));
294+
pipeline(new LettuceResult(getAsyncConnection().dispatch(cmd, expectedOutput, cmdArg)));
269295
return null;
270296
} else if (isQueueing()) {
271-
transaction(new LettuceResult(getAsyncConnection().dispatch(cmd, new ByteArrayOutput<byte[], byte[]>(CODEC),
272-
cmdArg)));
297+
transaction(new LettuceResult(getAsyncConnection().dispatch(cmd, expectedOutput, cmdArg)));
273298
return null;
274299
} else {
275-
return await(getAsyncConnection().dispatch(cmd, new ByteArrayOutput<byte[], byte[]>(CODEC), cmdArg));
300+
return await(getAsyncConnection().dispatch(cmd, expectedOutput, cmdArg));
276301
}
277302
} catch (RedisException ex) {
278303
throw convertLettuceAccessException(ex);
@@ -2938,4 +2963,202 @@ private ZStoreArgs zStoreArgs(Aggregate aggregate, int[] weights) {
29382963
return args;
29392964
}
29402965

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

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
}

src/test/java/org/springframework/data/redis/connection/jredis/JRedisConnectionIntegrationTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@
2121
import java.util.Arrays;
2222
import java.util.Collections;
2323
import java.util.HashSet;
24+
import java.util.List;
2425

2526
import org.apache.commons.pool.impl.GenericObjectPool.Config;
27+
import org.hamcrest.core.IsInstanceOf;
2628
import org.jredis.JRedis;
2729
import org.jredis.protocol.BulkResponse;
30+
import org.jredis.ri.alphazero.protocol.SyncProtocol.SyncMultiBulkResponse;
2831
import org.junit.After;
2932
import org.junit.Ignore;
3033
import org.junit.Test;
34+
import org.junit.internal.matchers.IsCollectionContaining;
3135
import org.junit.runner.RunWith;
3236
import org.springframework.dao.DataAccessException;
3337
import org.springframework.data.redis.RedisConnectionFailureException;
@@ -778,4 +782,23 @@ public void testGetTimeShouldRequestServerTime() {
778782
super.testGetTimeShouldRequestServerTime();
779783
}
780784

785+
/**
786+
* @see DATAREDIS-285
787+
*/
788+
@Test
789+
public void testExecuteShouldConvertArrayReplyCorrectly() {
790+
connection.set("spring", "awesome");
791+
connection.set("data", "cool");
792+
connection.set("redis", "supercalifragilisticexpialidocious");
793+
794+
Object result = connection.execute("MGET", "spring".getBytes(), "data".getBytes(), "redis".getBytes());
795+
796+
assertThat(result, IsInstanceOf.instanceOf(SyncMultiBulkResponse.class));
797+
798+
List<byte[]> data = ((SyncMultiBulkResponse) result).getMultiBulkData();
799+
assertThat(
800+
data,
801+
IsCollectionContaining.hasItems("awesome".getBytes(), "cool".getBytes(),
802+
"supercalifragilisticexpialidocious".getBytes()));
803+
}
781804
}

0 commit comments

Comments
 (0)