17
17
18
18
import static com .lambdaworks .redis .protocol .CommandType .*;
19
19
20
+ import java .lang .reflect .Constructor ;
20
21
import java .util .ArrayList ;
21
22
import java .util .Arrays ;
22
23
import java .util .Collections ;
24
+ import java .util .HashMap ;
23
25
import java .util .LinkedList ;
24
26
import java .util .List ;
25
27
import java .util .Map ;
26
28
import java .util .Properties ;
27
29
import java .util .Queue ;
28
30
import java .util .Set ;
31
+ import java .util .concurrent .ConcurrentHashMap ;
29
32
import java .util .concurrent .Future ;
30
33
import java .util .concurrent .TimeUnit ;
31
34
35
+ import org .springframework .beans .BeanUtils ;
32
36
import org .springframework .core .convert .converter .Converter ;
33
37
import org .springframework .dao .DataAccessException ;
34
38
import org .springframework .dao .InvalidDataAccessApiUsageException ;
46
50
import org .springframework .data .redis .connection .convert .Converters ;
47
51
import org .springframework .data .redis .connection .convert .TransactionResultConverter ;
48
52
import org .springframework .util .Assert ;
49
- import org .springframework .util .NumberUtils ;
53
+ import org .springframework .util .ClassUtils ;
50
54
import org .springframework .util .ObjectUtils ;
51
55
52
56
import com .lambdaworks .redis .RedisAsyncConnection ;
55
59
import com .lambdaworks .redis .SortArgs ;
56
60
import com .lambdaworks .redis .ZStoreArgs ;
57
61
import com .lambdaworks .redis .codec .RedisCodec ;
62
+ import com .lambdaworks .redis .output .BooleanOutput ;
58
63
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 ;
59
75
import com .lambdaworks .redis .protocol .Command ;
60
76
import com .lambdaworks .redis .protocol .CommandArgs ;
77
+ import com .lambdaworks .redis .protocol .CommandOutput ;
61
78
import com .lambdaworks .redis .protocol .CommandType ;
62
79
import com .lambdaworks .redis .pubsub .RedisPubSubConnection ;
63
80
72
89
public class LettuceConnection implements RedisConnection {
73
90
74
91
static final RedisCodec <byte [], byte []> CODEC = new BytesRedisCodec ();
92
+ private static final TypeHints typeHints = new TypeHints ();
75
93
76
94
private final com .lambdaworks .redis .RedisAsyncConnection <byte [], byte []> asyncSharedConn ;
77
95
private final com .lambdaworks .redis .RedisConnection <byte [], byte []> sharedConn ;
@@ -253,7 +271,23 @@ private Object await(Command cmd) {
253
271
return getAsyncConnection ().await (cmd , timeout , TimeUnit .MILLISECONDS );
254
272
}
255
273
274
+ @ Override
256
275
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
+
257
291
Assert .hasText (command , "a valid command needs to be specified" );
258
292
try {
259
293
String name = command .trim ().toUpperCase ();
@@ -264,16 +298,15 @@ public Object execute(String command, byte[]... args) {
264
298
cmdArg .addKeys (args );
265
299
}
266
300
301
+ CommandOutput expectedOutput = commandOutputTypeHint != null ? commandOutputTypeHint : typeHints .getTypeHint (cmd );
267
302
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 )));
270
304
return null ;
271
305
} 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 )));
274
307
return null ;
275
308
} else {
276
- return await (getAsyncConnection ().dispatch (cmd , new ByteArrayOutput < byte [], byte []>( CODEC ) , cmdArg ));
309
+ return await (getAsyncConnection ().dispatch (cmd , expectedOutput , cmdArg ));
277
310
}
278
311
} catch (RedisException ex ) {
279
312
throw convertLettuceAccessException (ex );
@@ -2776,7 +2809,7 @@ public Long time() {
2776
2809
* support the time command natively.
2777
2810
* see https://github.com/wg/lettuce/issues/19
2778
2811
*/
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 );
2780
2813
2781
2814
Assert .notEmpty (result , "Received invalid result from server. Expected 2 items in collection." );
2782
2815
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) {
2931
2964
return args ;
2932
2965
}
2933
2966
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
+
2934
3165
}
0 commit comments