29
29
30
30
import java .lang .reflect .Field ;
31
31
import java .lang .reflect .Method ;
32
- import java .util .ArrayList ;
32
+ import java .util .Arrays ;
33
33
import java .util .Collection ;
34
- import java .util .List ;
34
+ import java .util .Set ;
35
35
import java .util .function .Supplier ;
36
+ import java .util .stream .Collectors ;
36
37
37
38
import org .springframework .beans .DirectFieldAccessor ;
38
39
import org .springframework .lang .Nullable ;
39
40
import org .springframework .util .ClassUtils ;
40
41
import org .springframework .util .ReflectionUtils ;
41
42
42
43
/**
44
+ * Utility class to invoke arbitraty Jedis commands.
45
+ *
43
46
* @author Christoph Strobl
47
+ * @author Mark Paluch
44
48
* @since 2.1
45
49
*/
50
+ @ SuppressWarnings ({ "unchecked" , "ConstantConditions" })
46
51
class JedisClientUtils {
47
52
48
53
private static final Field CLIENT_FIELD ;
49
54
private static final Method SEND_COMMAND ;
50
55
private static final Method GET_RESPONSE ;
51
56
private static final Method PROTOCOL_SEND_COMMAND ;
57
+ private static final Set <String > KNOWN_COMMANDS ;
58
+ private static final Builder <Object > OBJECT_BUILDER ;
52
59
53
60
static {
54
61
@@ -60,12 +67,12 @@ class JedisClientUtils {
60
67
ReflectionUtils .makeAccessible (PROTOCOL_SEND_COMMAND );
61
68
62
69
try {
70
+
63
71
Class <?> commandType = ClassUtils .isPresent ("redis.clients.jedis.ProtocolCommand" , null )
64
72
? ClassUtils .forName ("redis.clients.jedis.ProtocolCommand" , null )
65
73
: ClassUtils .forName ("redis.clients.jedis.Protocol$Command" , null );
66
74
67
- SEND_COMMAND = ReflectionUtils .findMethod (Connection .class , "sendCommand" ,
68
- new Class [] { commandType , byte [][].class });
75
+ SEND_COMMAND = ReflectionUtils .findMethod (Connection .class , "sendCommand" , commandType , byte [][].class );
69
76
} catch (Exception e ) {
70
77
throw new NoClassDefFoundError (
71
78
"Could not find required flavor of command required by 'redis.clients.jedis.Connection#sendCommand'." );
@@ -75,24 +82,64 @@ class JedisClientUtils {
75
82
76
83
GET_RESPONSE = ReflectionUtils .findMethod (Queable .class , "getResponse" , Builder .class );
77
84
ReflectionUtils .makeAccessible (GET_RESPONSE );
85
+
86
+ KNOWN_COMMANDS = Arrays .stream (Command .values ()).map (Enum ::name ).collect (Collectors .toSet ());
87
+
88
+ OBJECT_BUILDER = new Builder <Object >() {
89
+ public Object build (Object data ) {
90
+ return data ;
91
+ }
92
+
93
+ public String toString () {
94
+ return "Object" ;
95
+ }
96
+ };
78
97
}
79
98
80
- @ Nullable
99
+ /**
100
+ * Execute an arbitrary on the supplied {@link Jedis} instance.
101
+ *
102
+ * @param command the command.
103
+ * @param keys must not be {@literal null}, may be empty.
104
+ * @param args must not be {@literal null}, may be empty.
105
+ * @param jedis must not be {@literal null}.
106
+ * @return the response, can be be {@literal null}.
107
+ */
108
+ @ Nullable // TODO: Change keys and args to byte[][]?
81
109
static <T > T execute (String command , Collection <byte []> keys , Collection <byte []> args , Supplier <Jedis > jedis ) {
82
110
83
- List <byte []> mArgs = new ArrayList <>(keys );
84
- mArgs .addAll (args );
111
+ byte [][] commandArgs = getCommandArguments (keys , args );
85
112
86
113
Client client = retrieveClient (jedis .get ());
87
- sendCommand (client , command , mArgs . toArray ( new byte [ mArgs . size ()][]) );
114
+ sendCommand (client , command , commandArgs );
88
115
89
116
return (T ) client .getOne ();
90
117
}
91
118
92
- static Client retrieveClient (Jedis jedis ) {
93
- return (Client ) ReflectionUtils .getField (CLIENT_FIELD , jedis );
119
+ private static byte [][] getCommandArguments (Collection <byte []> keys , Collection <byte []> args ) {
120
+
121
+ byte [][] commandArgs = new byte [keys .size () + args .size ()][];
122
+
123
+ int index = 0 ;
124
+ for (byte [] key : keys ) {
125
+ commandArgs [index ++] = key ;
126
+ }
127
+
128
+ for (byte [] arg : args ) {
129
+ commandArgs [index ++] = arg ;
130
+ }
131
+
132
+ return commandArgs ;
94
133
}
95
134
135
+ /**
136
+ * Send a Redis command and retrieve the {@link Client} for response retrieval.
137
+ *
138
+ * @param jedis
139
+ * @param command
140
+ * @param args
141
+ * @return
142
+ */
96
143
static Client sendCommand (Jedis jedis , String command , byte [][] args ) {
97
144
98
145
Client client = retrieveClient (jedis );
@@ -106,7 +153,7 @@ static Client sendCommand(Jedis jedis, String command, byte[][] args) {
106
153
return client ;
107
154
}
108
155
109
- static void sendCommand (Client client , String command , byte [][] args ) {
156
+ private static void sendCommand (Client client , String command , byte [][] args ) {
110
157
111
158
if (isKnownCommand (command )) {
112
159
ReflectionUtils .invokeMethod (SEND_COMMAND , client , Command .valueOf (command .trim ().toUpperCase ()), args );
@@ -115,8 +162,9 @@ static void sendCommand(Client client, String command, byte[][] args) {
115
162
}
116
163
}
117
164
118
- static void sendProtocolCommand (Client client , String command , byte [][] args ) {
165
+ private static void sendProtocolCommand (Client client , String command , byte [][] args ) {
119
166
167
+ // quite expensive to construct for each command invocation
120
168
DirectFieldAccessor dfa = new DirectFieldAccessor (client );
121
169
122
170
client .connect ();
@@ -125,34 +173,27 @@ static void sendProtocolCommand(Client client, String command, byte[][] args) {
125
173
ReflectionUtils .invokeMethod (PROTOCOL_SEND_COMMAND , null , os , SafeEncoder .encode (command ), args );
126
174
127
175
Integer pipelinedCommands = (Integer ) dfa .getPropertyValue ("pipelinedCommands" );
128
- dfa .setPropertyValue ("pipelinedCommands" , pipelinedCommands .intValue () + 1 );
129
- }
130
-
131
- static boolean isKnownCommand (String command ) {
132
-
133
- try {
134
- Command .valueOf (command );
135
- return true ;
136
- } catch (IllegalArgumentException e ) {
137
- return false ;
138
- }
176
+ dfa .setPropertyValue ("pipelinedCommands" , pipelinedCommands + 1 );
139
177
}
140
178
179
+ /**
180
+ * @param jedis the client instance.
181
+ * @return {@literal true} if the connection has entered {@literal MULTI} state.
182
+ */
141
183
static boolean isInMulti (Jedis jedis ) {
142
184
return retrieveClient (jedis ).isInMulti ();
143
185
}
144
186
145
- static Response <Object > getGetResponse (Object target ) {
146
-
147
- return (Response <Object >) ReflectionUtils .invokeMethod (GET_RESPONSE , target , new Builder <Object >() {
148
- public Object build (Object data ) {
149
- return data ;
150
- }
187
+ private static boolean isKnownCommand (String command ) {
188
+ return KNOWN_COMMANDS .contains (command );
189
+ }
151
190
152
- public String toString () {
153
- return "Object" ;
154
- }
155
- });
191
+ @ SuppressWarnings ({ "unchecked" , "ConstantConditions" })
192
+ static Response <Object > getGetResponse (Object target ) {
193
+ return (Response <Object >) ReflectionUtils .invokeMethod (GET_RESPONSE , target , OBJECT_BUILDER );
156
194
}
157
195
196
+ private static Client retrieveClient (Jedis jedis ) {
197
+ return (Client ) ReflectionUtils .getField (CLIENT_FIELD , jedis );
198
+ }
158
199
}
0 commit comments