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 ;
53
+ import org .springframework .util .ClassUtils ;
49
54
import org .springframework .util .ObjectUtils ;
50
55
51
56
import com .lambdaworks .redis .RedisAsyncConnection ;
54
59
import com .lambdaworks .redis .SortArgs ;
55
60
import com .lambdaworks .redis .ZStoreArgs ;
56
61
import com .lambdaworks .redis .codec .RedisCodec ;
62
+ import com .lambdaworks .redis .output .BooleanOutput ;
57
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 ;
58
75
import com .lambdaworks .redis .protocol .Command ;
59
76
import com .lambdaworks .redis .protocol .CommandArgs ;
77
+ import com .lambdaworks .redis .protocol .CommandOutput ;
60
78
import com .lambdaworks .redis .protocol .CommandType ;
61
79
import com .lambdaworks .redis .pubsub .RedisPubSubConnection ;
62
80
71
89
public class LettuceConnection implements RedisConnection {
72
90
73
91
static final RedisCodec <byte [], byte []> CODEC = new BytesRedisCodec ();
92
+ private static final TypeHints typeHints = new TypeHints ();
74
93
75
94
private final com .lambdaworks .redis .RedisAsyncConnection <byte [], byte []> asyncSharedConn ;
76
95
private final com .lambdaworks .redis .RedisConnection <byte [], byte []> sharedConn ;
@@ -252,7 +271,23 @@ private Object await(Command cmd) {
252
271
return getAsyncConnection ().await (cmd , timeout , TimeUnit .MILLISECONDS );
253
272
}
254
273
274
+ @ Override
255
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
+
256
291
Assert .hasText (command , "a valid command needs to be specified" );
257
292
try {
258
293
String name = command .trim ().toUpperCase ();
@@ -263,16 +298,15 @@ public Object execute(String command, byte[]... args) {
263
298
cmdArg .addKeys (args );
264
299
}
265
300
301
+ CommandOutput expectedOutput = commandOutputTypeHint != null ? commandOutputTypeHint : typeHints .getTypeHint (cmd );
266
302
if (isPipelined ()) {
267
- pipeline (new LettuceResult (getAsyncConnection ().dispatch (cmd , new ByteArrayOutput <byte [], byte []>(CODEC ),
268
- cmdArg )));
303
+ pipeline (new LettuceResult (getAsyncConnection ().dispatch (cmd , expectedOutput , cmdArg )));
269
304
return null ;
270
305
} else if (isQueueing ()) {
271
- transaction (new LettuceResult (getAsyncConnection ().dispatch (cmd , new ByteArrayOutput <byte [], byte []>(CODEC ),
272
- cmdArg )));
306
+ transaction (new LettuceResult (getAsyncConnection ().dispatch (cmd , expectedOutput , cmdArg )));
273
307
return null ;
274
308
} else {
275
- return await (getAsyncConnection ().dispatch (cmd , new ByteArrayOutput < byte [], byte []>( CODEC ) , cmdArg ));
309
+ return await (getAsyncConnection ().dispatch (cmd , expectedOutput , cmdArg ));
276
310
}
277
311
} catch (RedisException ex ) {
278
312
throw convertLettuceAccessException (ex );
@@ -2938,4 +2972,202 @@ private ZStoreArgs zStoreArgs(Aggregate aggregate, int[] weights) {
2938
2972
return args ;
2939
2973
}
2940
2974
2975
+ /**
2976
+ * {@link TypeHints} provide {@link CommandOutput} information for a given {@link CommandType}.
2977
+ *
2978
+ * @since 1.2.1
2979
+ */
2980
+ static class TypeHints {
2981
+
2982
+ @ SuppressWarnings ("rawtypes" )//
2983
+ private static final Map <CommandType , Class <? extends CommandOutput >> COMMAND_OUTPUT_TYPE_MAPPING = new HashMap <CommandType , Class <? extends CommandOutput >>();
2984
+
2985
+ @ SuppressWarnings ("rawtypes" )//
2986
+ private static final Map <Class <?>, Constructor <CommandOutput >> CONSTRUCTORS = new ConcurrentHashMap <Class <?>, Constructor <CommandOutput >>();
2987
+
2988
+ {
2989
+ // INTEGER
2990
+ COMMAND_OUTPUT_TYPE_MAPPING .put (BITCOUNT , IntegerOutput .class );
2991
+ COMMAND_OUTPUT_TYPE_MAPPING .put (BITOP , IntegerOutput .class );
2992
+ COMMAND_OUTPUT_TYPE_MAPPING .put (DBSIZE , IntegerOutput .class );
2993
+ COMMAND_OUTPUT_TYPE_MAPPING .put (DECR , IntegerOutput .class );
2994
+ COMMAND_OUTPUT_TYPE_MAPPING .put (DECRBY , IntegerOutput .class );
2995
+ COMMAND_OUTPUT_TYPE_MAPPING .put (DEL , IntegerOutput .class );
2996
+ COMMAND_OUTPUT_TYPE_MAPPING .put (GETBIT , IntegerOutput .class );
2997
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HDEL , IntegerOutput .class );
2998
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HINCRBY , IntegerOutput .class );
2999
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HLEN , IntegerOutput .class );
3000
+ COMMAND_OUTPUT_TYPE_MAPPING .put (INCR , IntegerOutput .class );
3001
+ COMMAND_OUTPUT_TYPE_MAPPING .put (INCRBY , IntegerOutput .class );
3002
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LINSERT , IntegerOutput .class );
3003
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LLEN , IntegerOutput .class );
3004
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LPUSH , IntegerOutput .class );
3005
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LPUSHX , IntegerOutput .class );
3006
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LREM , IntegerOutput .class );
3007
+ COMMAND_OUTPUT_TYPE_MAPPING .put (PTTL , IntegerOutput .class );
3008
+ COMMAND_OUTPUT_TYPE_MAPPING .put (PUBLISH , IntegerOutput .class );
3009
+ COMMAND_OUTPUT_TYPE_MAPPING .put (RPUSH , IntegerOutput .class );
3010
+ COMMAND_OUTPUT_TYPE_MAPPING .put (RPUSHX , IntegerOutput .class );
3011
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SADD , IntegerOutput .class );
3012
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SCARD , IntegerOutput .class );
3013
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SDIFFSTORE , IntegerOutput .class );
3014
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SETBIT , IntegerOutput .class );
3015
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SETRANGE , IntegerOutput .class );
3016
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SINTERSTORE , IntegerOutput .class );
3017
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SREM , IntegerOutput .class );
3018
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SUNIONSTORE , IntegerOutput .class );
3019
+ COMMAND_OUTPUT_TYPE_MAPPING .put (STRLEN , IntegerOutput .class );
3020
+ COMMAND_OUTPUT_TYPE_MAPPING .put (TTL , IntegerOutput .class );
3021
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZADD , IntegerOutput .class );
3022
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZCOUNT , IntegerOutput .class );
3023
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZINTERSTORE , IntegerOutput .class );
3024
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZRANK , IntegerOutput .class );
3025
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZREM , IntegerOutput .class );
3026
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZREMRANGEBYRANK , IntegerOutput .class );
3027
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZREMRANGEBYSCORE , IntegerOutput .class );
3028
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZREVRANK , IntegerOutput .class );
3029
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZUNIONSTORE , IntegerOutput .class );
3030
+
3031
+ // DOUBLE
3032
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HINCRBYFLOAT , DoubleOutput .class );
3033
+ COMMAND_OUTPUT_TYPE_MAPPING .put (INCRBYFLOAT , DoubleOutput .class );
3034
+ COMMAND_OUTPUT_TYPE_MAPPING .put (MGET , ValueListOutput .class );
3035
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZINCRBY , DoubleOutput .class );
3036
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZSCORE , DoubleOutput .class );
3037
+
3038
+ // MAP
3039
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HGETALL , MapOutput .class );
3040
+
3041
+ // KEY LIST
3042
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HKEYS , KeyListOutput .class );
3043
+ COMMAND_OUTPUT_TYPE_MAPPING .put (KEYS , KeyListOutput .class );
3044
+
3045
+ // KEY VALUE
3046
+ COMMAND_OUTPUT_TYPE_MAPPING .put (BRPOP , KeyValueOutput .class );
3047
+
3048
+ // SINGLE VALUE
3049
+ COMMAND_OUTPUT_TYPE_MAPPING .put (BRPOPLPUSH , ValueOutput .class );
3050
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ECHO , ValueOutput .class );
3051
+ COMMAND_OUTPUT_TYPE_MAPPING .put (GET , ValueOutput .class );
3052
+ COMMAND_OUTPUT_TYPE_MAPPING .put (GETRANGE , ValueOutput .class );
3053
+ COMMAND_OUTPUT_TYPE_MAPPING .put (GETSET , ValueOutput .class );
3054
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HGET , ValueOutput .class );
3055
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LINDEX , ValueOutput .class );
3056
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LPOP , ValueOutput .class );
3057
+ COMMAND_OUTPUT_TYPE_MAPPING .put (RANDOMKEY , ValueOutput .class );
3058
+ COMMAND_OUTPUT_TYPE_MAPPING .put (RENAME , ValueOutput .class );
3059
+ COMMAND_OUTPUT_TYPE_MAPPING .put (RPOP , ValueOutput .class );
3060
+ COMMAND_OUTPUT_TYPE_MAPPING .put (RPOPLPUSH , ValueOutput .class );
3061
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SPOP , ValueOutput .class );
3062
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SRANDMEMBER , ValueOutput .class );
3063
+
3064
+ // STATUS VALUE
3065
+ COMMAND_OUTPUT_TYPE_MAPPING .put (BGREWRITEAOF , StatusOutput .class );
3066
+ COMMAND_OUTPUT_TYPE_MAPPING .put (BGSAVE , StatusOutput .class );
3067
+ COMMAND_OUTPUT_TYPE_MAPPING .put (CLIENT , StatusOutput .class );
3068
+ COMMAND_OUTPUT_TYPE_MAPPING .put (DEBUG , StatusOutput .class );
3069
+ COMMAND_OUTPUT_TYPE_MAPPING .put (DISCARD , StatusOutput .class );
3070
+ COMMAND_OUTPUT_TYPE_MAPPING .put (FLUSHALL , StatusOutput .class );
3071
+ COMMAND_OUTPUT_TYPE_MAPPING .put (FLUSHDB , StatusOutput .class );
3072
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HMSET , StatusOutput .class );
3073
+ COMMAND_OUTPUT_TYPE_MAPPING .put (INFO , StatusOutput .class );
3074
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LSET , StatusOutput .class );
3075
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LTRIM , StatusOutput .class );
3076
+ COMMAND_OUTPUT_TYPE_MAPPING .put (MIGRATE , StatusOutput .class );
3077
+ COMMAND_OUTPUT_TYPE_MAPPING .put (MSET , StatusOutput .class );
3078
+ COMMAND_OUTPUT_TYPE_MAPPING .put (QUIT , StatusOutput .class );
3079
+ COMMAND_OUTPUT_TYPE_MAPPING .put (RESTORE , StatusOutput .class );
3080
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SAVE , StatusOutput .class );
3081
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SELECT , StatusOutput .class );
3082
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SET , StatusOutput .class );
3083
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SETEX , StatusOutput .class );
3084
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SHUTDOWN , StatusOutput .class );
3085
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SLAVEOF , StatusOutput .class );
3086
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SYNC , StatusOutput .class );
3087
+ COMMAND_OUTPUT_TYPE_MAPPING .put (TYPE , StatusOutput .class );
3088
+ COMMAND_OUTPUT_TYPE_MAPPING .put (WATCH , StatusOutput .class );
3089
+ COMMAND_OUTPUT_TYPE_MAPPING .put (UNWATCH , StatusOutput .class );
3090
+
3091
+ // VALUE LIST
3092
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HMGET , ValueListOutput .class );
3093
+ COMMAND_OUTPUT_TYPE_MAPPING .put (MGET , ValueListOutput .class );
3094
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HVALS , ValueListOutput .class );
3095
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LRANGE , ValueListOutput .class );
3096
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SORT , ValueListOutput .class );
3097
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZRANGE , ValueListOutput .class );
3098
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZRANGEBYSCORE , ValueListOutput .class );
3099
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZREVRANGE , ValueListOutput .class );
3100
+ COMMAND_OUTPUT_TYPE_MAPPING .put (ZREVRANGEBYSCORE , ValueListOutput .class );
3101
+
3102
+ // BOOLEAN
3103
+ COMMAND_OUTPUT_TYPE_MAPPING .put (EXISTS , BooleanOutput .class );
3104
+ COMMAND_OUTPUT_TYPE_MAPPING .put (EXPIRE , BooleanOutput .class );
3105
+ COMMAND_OUTPUT_TYPE_MAPPING .put (EXPIREAT , BooleanOutput .class );
3106
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HEXISTS , BooleanOutput .class );
3107
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HSET , BooleanOutput .class );
3108
+ COMMAND_OUTPUT_TYPE_MAPPING .put (HSETNX , BooleanOutput .class );
3109
+ COMMAND_OUTPUT_TYPE_MAPPING .put (MOVE , BooleanOutput .class );
3110
+ COMMAND_OUTPUT_TYPE_MAPPING .put (MSETNX , BooleanOutput .class );
3111
+ COMMAND_OUTPUT_TYPE_MAPPING .put (PERSIST , BooleanOutput .class );
3112
+ COMMAND_OUTPUT_TYPE_MAPPING .put (PEXPIRE , BooleanOutput .class );
3113
+ COMMAND_OUTPUT_TYPE_MAPPING .put (PEXPIREAT , BooleanOutput .class );
3114
+ COMMAND_OUTPUT_TYPE_MAPPING .put (RENAMENX , BooleanOutput .class );
3115
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SETNX , BooleanOutput .class );
3116
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SISMEMBER , BooleanOutput .class );
3117
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SMOVE , BooleanOutput .class );
3118
+
3119
+ // MULTI
3120
+ COMMAND_OUTPUT_TYPE_MAPPING .put (EXEC , MultiOutput .class );
3121
+ COMMAND_OUTPUT_TYPE_MAPPING .put (MULTI , MultiOutput .class );
3122
+
3123
+ // DATE
3124
+ COMMAND_OUTPUT_TYPE_MAPPING .put (LASTSAVE , DateOutput .class );
3125
+
3126
+ // VALUE SET
3127
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SDIFF , ValueSetOutput .class );
3128
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SINTER , ValueSetOutput .class );
3129
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SMEMBERS , ValueSetOutput .class );
3130
+ COMMAND_OUTPUT_TYPE_MAPPING .put (SUNION , ValueSetOutput .class );
3131
+ }
3132
+
3133
+ /**
3134
+ * Returns the {@link CommandOutput} mapped for given {@link CommandType} or {@link ByteArrayOutput} as default.
3135
+ *
3136
+ * @param type
3137
+ * @return {@link ByteArrayOutput} as default when no matching {@link CommandOutput} available.
3138
+ */
3139
+ @ SuppressWarnings ("rawtypes" )
3140
+ public CommandOutput getTypeHint (CommandType type ) {
3141
+ return getTypeHint (type , new ByteArrayOutput <byte [], byte []>(CODEC ));
3142
+ }
3143
+
3144
+ /**
3145
+ * Returns the {@link CommandOutput} mapped for given {@link CommandType} given {@link CommandOutput} as default.
3146
+ *
3147
+ * @param type
3148
+ * @return
3149
+ */
3150
+ @ SuppressWarnings ("rawtypes" )
3151
+ public CommandOutput getTypeHint (CommandType type , CommandOutput defaultType ) {
3152
+
3153
+ if (type == null || !COMMAND_OUTPUT_TYPE_MAPPING .containsKey (type )) {
3154
+ return defaultType ;
3155
+ }
3156
+ CommandOutput <?, ?, ?> outputType = instanciateCommandOutput (COMMAND_OUTPUT_TYPE_MAPPING .get (type ));
3157
+ return outputType != null ? outputType : defaultType ;
3158
+ }
3159
+
3160
+ @ SuppressWarnings ({ "rawtypes" , "unchecked" })
3161
+ private CommandOutput <?, ?, ?> instanciateCommandOutput (Class <? extends CommandOutput > type ) {
3162
+
3163
+ Assert .notNull (type , "Cannot create instance for 'null' type." );
3164
+ Constructor <CommandOutput > constructor = CONSTRUCTORS .get (type );
3165
+ if (constructor == null ) {
3166
+ constructor = (Constructor <CommandOutput >) ClassUtils .getConstructorIfAvailable (type , RedisCodec .class );
3167
+ CONSTRUCTORS .put (type , constructor );
3168
+ }
3169
+ return BeanUtils .instantiateClass (constructor , CODEC );
3170
+ }
3171
+ }
3172
+
2941
3173
}
0 commit comments