Skip to content

Commit 2679405

Browse files
christophstroblThomas Darimont
authored and
Thomas Darimont
committed
DATAREDIS-268 - Add support for 'CLIENT LIST'.
'RedisConnection' and 'RedisTemplate' have been extended by 'getClientList' retrieving client informations from redis. 'RedisClientInfo' provides access via specified getters as well as an more general approach directly using the keys (like 'qubuf'). The operation is available for 'jedis', 'lettuce' and 'srp'. Original Pull Request: #53
1 parent 4bf438f commit 2679405

28 files changed

+937
-17
lines changed

docs/src/reference/docbook/appendix/appendix-command-reference.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#<?xml version="1.0" encoding="UTF-8"?>
1+
<?xml version="1.0" encoding="UTF-8"?>
22
<appendix xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="appendix-command-reference" xmlns:xi="http://www.w3.org/2001/XInclude">
33
<title>Spring Data Redis Supported Commands</title>
44

@@ -25,7 +25,7 @@
2525
<row><entry><code>BRPOPLPUSH</code></entry><entry>X</entry></row>
2626
<row><entry><code>CLIENT KILL</code></entry><entry>X</entry></row>
2727
<row><entry><code>CLIENT GETNAME</code></entry><entry>X</entry></row>
28-
<row><entry><code>CLIENT LIST</code></entry><entry>-</entry></row>
28+
<row><entry><code>CLIENT LIST</code></entry><entry>X</entry></row>
2929
<row><entry><code>CLIENT SETNAME</code></entry><entry>X</entry></row>
3030
<row><entry><code>CONFIG GET</code></entry><entry>X</entry></row>
3131
<row><entry><code>CONFIG RESETSTAT</code></entry><entry>X</entry></row>

src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.data.redis.connection.convert.ListConverter;
3333
import org.springframework.data.redis.connection.convert.MapConverter;
3434
import org.springframework.data.redis.connection.convert.SetConverter;
35+
import org.springframework.data.redis.core.types.RedisClientInfo;
3536
import org.springframework.data.redis.serializer.RedisSerializer;
3637
import org.springframework.data.redis.serializer.StringRedisSerializer;
3738
import org.springframework.util.Assert;
@@ -2191,6 +2192,11 @@ public Long time() {
21912192
return this.delegate.time();
21922193
}
21932194

2195+
@Override
2196+
public List<RedisClientInfo> getClientList() {
2197+
return this.delegate.getClientList();
2198+
}
2199+
21942200
/**
21952201
* Specifies if pipelined and tx results should be deserialized to Strings. If false, results of
21962202
* {@link #closePipeline()} and {@link #exec()} will be of the type returned by the underlying connection

src/main/java/org/springframework/data/redis/connection/RedisServerCommands.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.util.List;
1919
import java.util.Properties;
2020

21+
import org.springframework.data.redis.core.types.RedisClientInfo;
22+
2123
/**
2224
* Server-specific commands supported by Redis.
2325
*
@@ -186,4 +188,13 @@ public enum ShutdownOption {
186188
* @since 1.3
187189
*/
188190
String getClientName();
191+
192+
/**
193+
* Request information and statistics about connected clients.
194+
*
195+
* @return {@link List} of {@link RedisClientInfo} objects.
196+
* @since 1.3
197+
* @see http://redis.io/commands/client-list
198+
*/
199+
List<RedisClientInfo> getClientList();
189200
}

src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.springframework.data.redis.core.RedisCallback;
2424
import org.springframework.data.redis.core.StringRedisTemplate;
25+
import org.springframework.data.redis.core.types.RedisClientInfo;
2526
import org.springframework.data.redis.serializer.RedisSerializer;
2627

2728
/**
@@ -301,4 +302,10 @@ public interface StringTuple extends Tuple {
301302
* @sice 1.3
302303
*/
303304
void setClientName(String name);
305+
306+
/**
307+
* @see RedisConnection#getClientList()
308+
* @since 1.3
309+
*/
310+
List<RedisClientInfo> getClientList();
304311
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.connection.convert;
17+
18+
import java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.List;
21+
22+
import org.springframework.core.convert.converter.Converter;
23+
import org.springframework.data.redis.core.types.RedisClientInfo;
24+
import org.springframework.data.redis.core.types.RedisClientInfo.RedisClientInfoBuilder;
25+
26+
/**
27+
* {@link Converter} implementation to create one {@link RedisClientInfo} per line entry in given {@link String} array.
28+
*
29+
* <pre>
30+
* ## sample of single line
31+
* addr=127.0.0.1:60311 fd=6 name= age=4059 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
32+
* </pre>
33+
*
34+
* @author Christoph Strobl
35+
* @since 1.3
36+
*/
37+
public class StringToRedisClientInfoConverter implements Converter<String[], List<RedisClientInfo>> {
38+
39+
@Override
40+
public List<RedisClientInfo> convert(String[] lines) {
41+
42+
if (lines == null) {
43+
return Collections.emptyList();
44+
}
45+
List<RedisClientInfo> infos = new ArrayList<RedisClientInfo>(lines.length);
46+
for (String line : lines) {
47+
infos.add(RedisClientInfoBuilder.fromString(line));
48+
}
49+
return infos;
50+
}
51+
52+
}

src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.data.redis.connection.Subscription;
4343
import org.springframework.data.redis.connection.convert.Converters;
4444
import org.springframework.data.redis.connection.convert.TransactionResultConverter;
45+
import org.springframework.data.redis.core.types.RedisClientInfo;
4546
import org.springframework.util.Assert;
4647
import org.springframework.util.ObjectUtils;
4748
import org.springframework.util.ReflectionUtils;
@@ -2854,6 +2855,18 @@ public String getClientName() {
28542855
return jedis.clientGetname();
28552856
}
28562857

2858+
/*
2859+
* @see org.springframework.data.redis.connection.RedisServerCommands#getClientName()
2860+
*/
2861+
@Override
2862+
public List<RedisClientInfo> getClientList() {
2863+
2864+
if (isQueueing() || isPipelined()) {
2865+
throw new UnsupportedOperationException("'CLIENT LIST' is not supported in in pipeline / multi mode.");
2866+
}
2867+
return JedisConverters.toListOfRedisClientInformation(this.jedis.clientList());
2868+
}
2869+
28572870
/**
28582871
* Specifies if pipelined results should be converted to the expected data type. If false, results of
28592872
* {@link #closePipeline()} and {@link #exec()} will be of the type returned by the Jedis driver

src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013 the original author or authors.
2+
* Copyright 2013-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.redis.connection.jedis;
1717

18+
import java.util.Collections;
19+
import java.util.List;
1820
import java.util.Map;
1921
import java.util.Set;
2022

@@ -31,7 +33,10 @@
3133
import org.springframework.data.redis.connection.convert.ListConverter;
3234
import org.springframework.data.redis.connection.convert.MapConverter;
3335
import org.springframework.data.redis.connection.convert.SetConverter;
36+
import org.springframework.data.redis.connection.convert.StringToRedisClientInfoConverter;
37+
import org.springframework.data.redis.core.types.RedisClientInfo;
3438
import org.springframework.util.Assert;
39+
import org.springframework.util.StringUtils;
3540

3641
import redis.clients.jedis.BinaryClient.LIST_POSITION;
3742
import redis.clients.jedis.BitOP;
@@ -42,6 +47,7 @@
4247
* Jedis type converters
4348
*
4449
* @author Jennifer Hickey
50+
* @author Christoph Strobl
4551
*/
4652
abstract public class JedisConverters extends Converters {
4753

@@ -51,6 +57,7 @@ abstract public class JedisConverters extends Converters {
5157
private static final MapConverter<String, byte[]> STRING_MAP_TO_BYTE_MAP;
5258
private static final SetConverter<redis.clients.jedis.Tuple, Tuple> TUPLE_SET_TO_TUPLE_SET;
5359
private static final Converter<Exception, DataAccessException> EXCEPTION_CONVERTER = new JedisExceptionConverter();
60+
private static final Converter<String[], List<RedisClientInfo>> STRING_TO_CLIENT_INFO_CONVERTER = new StringToRedisClientInfoConverter();
5461

5562
static {
5663
STRING_TO_BYTES = new Converter<String, byte[]>() {
@@ -114,6 +121,19 @@ public static String toString(byte[] source) {
114121
return source == null ? null : SafeEncoder.encode(source);
115122
}
116123

124+
/**
125+
* @param source
126+
* @return
127+
* @since 1.3
128+
*/
129+
public static List<RedisClientInfo> toListOfRedisClientInformation(String source) {
130+
131+
if (!StringUtils.hasText(source)) {
132+
return Collections.emptyList();
133+
}
134+
return STRING_TO_CLIENT_INFO_CONVERTER.convert(source.split("\\r?\\n"));
135+
}
136+
117137
public static DataAccessException toDataAccessException(Exception ex) {
118138
return EXCEPTION_CONVERTER.convert(ex);
119139
}

src/main/java/org/springframework/data/redis/connection/jredis/JredisConnection.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.data.redis.connection.ReturnType;
4444
import org.springframework.data.redis.connection.SortParameters;
4545
import org.springframework.data.redis.connection.Subscription;
46+
import org.springframework.data.redis.core.types.RedisClientInfo;
4647
import org.springframework.util.Assert;
4748
import org.springframework.util.ObjectUtils;
4849
import org.springframework.util.ReflectionUtils;
@@ -1204,4 +1205,8 @@ public void setClientName(byte[] name) {
12041205
public String getClientName() {
12051206
throw new UnsupportedOperationException("The 'CLIENT GETNAME' command is not supported by the JRedis driver.");
12061207
}
1208+
1209+
public List<RedisClientInfo> getClientList() {
1210+
throw new UnsupportedOperationException();
1211+
}
12071212
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.springframework.data.redis.connection.Subscription;
5050
import org.springframework.data.redis.connection.convert.Converters;
5151
import org.springframework.data.redis.connection.convert.TransactionResultConverter;
52+
import org.springframework.data.redis.core.types.RedisClientInfo;
5253
import org.springframework.util.Assert;
5354
import org.springframework.util.ClassUtils;
5455
import org.springframework.util.ObjectUtils;
@@ -2959,6 +2960,21 @@ public String getClientName() {
29592960
}
29602961
}
29612962

2963+
@Override
2964+
public List<RedisClientInfo> getClientList() {
2965+
2966+
if (isPipelined()) {
2967+
throw new UnsupportedOperationException("Cannot be called in pipeline mode.");
2968+
}
2969+
if (isQueueing()) {
2970+
transaction(new LettuceTxResult(getAsyncConnection().clientList(),
2971+
LettuceConverters.stringToRedisClientListConverter()));
2972+
return null;
2973+
}
2974+
2975+
return LettuceConverters.toListOfRedisClientInformation(getConnection().clientList());
2976+
}
2977+
29622978
/**
29632979
* Specifies if pipelined and transaction results should be converted to the expected data type. If false, results of
29642980
* {@link #closePipeline()} and {@link #exec()} will be of the type returned by the Lettuce driver

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.Arrays;
20+
import java.util.Collections;
2021
import java.util.Date;
2122
import java.util.LinkedHashSet;
2223
import java.util.List;
@@ -31,7 +32,10 @@
3132
import org.springframework.data.redis.connection.SortParameters;
3233
import org.springframework.data.redis.connection.SortParameters.Order;
3334
import org.springframework.data.redis.connection.convert.Converters;
35+
import org.springframework.data.redis.connection.convert.StringToRedisClientInfoConverter;
36+
import org.springframework.data.redis.core.types.RedisClientInfo;
3437
import org.springframework.util.Assert;
38+
import org.springframework.util.StringUtils;
3539

3640
import com.lambdaworks.redis.KeyValue;
3741
import com.lambdaworks.redis.ScoredValue;
@@ -55,6 +59,8 @@ abstract public class LettuceConverters extends Converters {
5559
private static final Converter<ScoredValue<byte[]>, Tuple> SCORED_VALUE_TO_TUPLE;
5660
private static final Converter<Exception, DataAccessException> EXCEPTION_CONVERTER = new LettuceExceptionConverter();
5761

62+
private static final Converter<String[], List<RedisClientInfo>> STRING_TO_LIST_OF_CLIENT_INFO = new StringToRedisClientInfoConverter();
63+
5864
static {
5965
DATE_TO_LONG = new Converter<Date, Long>() {
6066
public Long convert(Date source) {
@@ -115,6 +121,20 @@ public Tuple convert(ScoredValue<byte[]> source) {
115121
};
116122
}
117123

124+
public static Converter<String, List<RedisClientInfo>> stringToRedisClientListConverter() {
125+
return new Converter<String, List<RedisClientInfo>>() {
126+
127+
@Override
128+
public List<RedisClientInfo> convert(String source) {
129+
if (!StringUtils.hasText(source)) {
130+
return Collections.emptyList();
131+
}
132+
133+
return STRING_TO_LIST_OF_CLIENT_INFO.convert(source.split("\\r?\\n"));
134+
}
135+
};
136+
}
137+
118138
public static Converter<Date, Long> dateToLong() {
119139
return DATE_TO_LONG;
120140
}
@@ -235,4 +255,8 @@ public static SortArgs toSortArgs(SortParameters params) {
235255
}
236256
return args;
237257
}
258+
259+
public static List<RedisClientInfo> toListOfRedisClientInformation(String clientList) {
260+
return stringToRedisClientListConverter().convert(clientList);
261+
}
238262
}

src/main/java/org/springframework/data/redis/connection/srp/SrpConnection.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.data.redis.connection.ReturnType;
4141
import org.springframework.data.redis.connection.SortParameters;
4242
import org.springframework.data.redis.connection.Subscription;
43+
import org.springframework.data.redis.core.types.RedisClientInfo;
4344
import org.springframework.util.Assert;
4445

4546
import redis.Command;
@@ -2280,6 +2281,19 @@ public String getClientName() {
22802281
}
22812282
}
22822283

2284+
@Override
2285+
public List<RedisClientInfo> getClientList() {
2286+
if (isQueueing()) {
2287+
throw new UnsupportedOperationException();
2288+
}
2289+
if (isPipelined()) {
2290+
pipeline(new SrpGenericResult(pipeline.client_list(), SrpConverters.replyToListOfRedisClientInfo()));
2291+
return null;
2292+
}
2293+
2294+
return SrpConverters.toListOfRedisClientInformation(this.client.client_list());
2295+
}
2296+
22832297
private List<Object> closeTransaction() {
22842298
List<Object> results = Collections.emptyList();
22852299
if (txTracker != null) {

0 commit comments

Comments
 (0)