Refactor converters.

Prefer lambda-style converters. Replace indirections via Converters API with method references.

Original Pull Request: #1960
This commit is contained in:
Mark Paluch
2021-02-08 14:37:17 +01:00
committed by Christoph Strobl
parent c7eef8fcff
commit 53ce84851c
8 changed files with 329 additions and 371 deletions

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.convert;
import java.io.StringReader;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.ArrayList;
@@ -37,6 +38,7 @@ import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Metrics;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisClusterNode;
import org.springframework.data.redis.connection.RedisClusterNode.Flag;
@@ -73,168 +75,50 @@ abstract public class Converters {
private static final byte[] ONE = new byte[] { '1' };
private static final byte[] ZERO = new byte[] { '0' };
private static final String CLUSTER_NODES_LINE_SEPARATOR = "\n";
private static final Converter<String, Properties> STRING_TO_PROPS = new StringToPropertiesConverter();
private static final Converter<Long, Boolean> LONG_TO_BOOLEAN = new LongToBooleanConverter();
private static final Converter<String, DataType> STRING_TO_DATA_TYPE = new StringToDataTypeConverter();
private static final Converter<Map<?, ?>, Properties> MAP_TO_PROPERTIES = MapToPropertiesConverter.INSTANCE;
private static final Converter<String, RedisClusterNode> STRING_TO_CLUSTER_NODE_CONVERTER;
private static final Converter<List<String>, Properties> STRING_LIST_TO_PROPERTIES_CONVERTER;
private static final Map<String, Flag> flagLookupMap;
static {
flagLookupMap = new LinkedHashMap<>(Flag.values().length, 1);
for (Flag flag : Flag.values()) {
flagLookupMap.put(flag.getRaw(), flag);
}
STRING_TO_CLUSTER_NODE_CONVERTER = new Converter<String, RedisClusterNode>() {
static final int ID_INDEX = 0;
static final int HOST_PORT_INDEX = 1;
static final int FLAGS_INDEX = 2;
static final int MASTER_ID_INDEX = 3;
static final int LINK_STATE_INDEX = 7;
static final int SLOTS_INDEX = 8;
@Override
public RedisClusterNode convert(String source) {
String[] args = source.split(" ");
String[] hostAndPort = StringUtils.split(args[HOST_PORT_INDEX], ":");
Assert.notNull(hostAndPort, "CusterNode information does not define host and port!");
SlotRange range = parseSlotRange(args);
Set<Flag> flags = parseFlags(args);
String portPart = hostAndPort[1];
if (portPart.contains("@")) {
portPart = portPart.substring(0, portPart.indexOf('@'));
}
RedisClusterNodeBuilder nodeBuilder = RedisClusterNode.newRedisClusterNode()
.listeningAt(hostAndPort[0], Integer.valueOf(portPart)) //
.withId(args[ID_INDEX]) //
.promotedAs(flags.contains(Flag.MASTER) ? NodeType.MASTER : NodeType.SLAVE) //
.serving(range) //
.withFlags(flags) //
.linkState(parseLinkState(args));
if (!args[MASTER_ID_INDEX].isEmpty() && !args[MASTER_ID_INDEX].startsWith("-")) {
nodeBuilder.slaveOf(args[MASTER_ID_INDEX]);
}
return nodeBuilder.build();
}
private Set<Flag> parseFlags(String[] args) {
String raw = args[FLAGS_INDEX];
Set<Flag> flags = new LinkedHashSet<>(8, 1);
if (StringUtils.hasText(raw)) {
for (String flag : raw.split(",")) {
flags.add(flagLookupMap.get(flag));
}
}
return flags;
}
private LinkState parseLinkState(String[] args) {
String raw = args[LINK_STATE_INDEX];
if (StringUtils.hasText(raw)) {
return LinkState.valueOf(raw.toUpperCase());
}
return LinkState.DISCONNECTED;
}
private SlotRange parseSlotRange(String[] args) {
Set<Integer> slots = new LinkedHashSet<>();
for (int i = SLOTS_INDEX; i < args.length; i++) {
String raw = args[i];
if (raw.startsWith("[")) {
continue;
}
if (raw.contains("-")) {
String[] slotRange = StringUtils.split(raw, "-");
if (slotRange != null) {
int from = Integer.valueOf(slotRange[0]);
int to = Integer.valueOf(slotRange[1]);
for (int slot = from; slot <= to; slot++) {
slots.add(slot);
}
}
} else {
slots.add(Integer.valueOf(raw));
}
}
return new SlotRange(slots);
}
};
STRING_LIST_TO_PROPERTIES_CONVERTER = input -> {
Assert.notNull(input, "Input list must not be null!");
Assert.isTrue(input.size() % 2 == 0, "Input list must contain an even number of entries!");
Properties properties = new Properties();
for (int i = 0; i < input.size(); i += 2) {
properties.setProperty(input.get(i), input.get(i + 1));
}
return properties;
};
}
public static Boolean stringToBoolean(String s) {
return stringToBooleanConverter().convert(s);
public static Boolean stringToBoolean(String source) {
return ObjectUtils.nullSafeEquals("OK", source);
}
public static Converter<String, Boolean> stringToBooleanConverter() {
return (source) -> ObjectUtils.nullSafeEquals("OK", source);
return Converters::stringToBoolean;
}
public static Converter<String, Properties> stringToProps() {
return STRING_TO_PROPS;
return Converters::toProperties;
}
public static Converter<Long, Boolean> longToBoolean() {
return LONG_TO_BOOLEAN;
return Converters::toBoolean;
}
public static Converter<String, DataType> stringToDataType() {
return STRING_TO_DATA_TYPE;
return Converters::toDataType;
}
public static Properties toProperties(String source) {
return STRING_TO_PROPS.convert(source);
Properties info = new Properties();
try (StringReader stringReader = new StringReader(source)) {
info.load(stringReader);
} catch (Exception ex) {
throw new RedisSystemException("Cannot read Redis info", ex);
}
return info;
}
public static Properties toProperties(Map<?, ?> source) {
Properties properties = MAP_TO_PROPERTIES.convert(source);
return properties != null ? properties : new Properties();
Properties target = new Properties();
target.putAll(source);
return target;
}
public static Boolean toBoolean(Long source) {
return LONG_TO_BOOLEAN.convert(source);
public static Boolean toBoolean(@Nullable Long source) {
return source != null && source == 1L;
}
public static DataType toDataType(String source) {
return STRING_TO_DATA_TYPE.convert(source);
return DataType.fromCode(source);
}
public static byte[] toBit(Boolean source) {
@@ -249,7 +133,7 @@ abstract public class Converters {
* @since 1.7
*/
protected static RedisClusterNode toClusterNode(String clusterNodesLine) {
return STRING_TO_CLUSTER_NODE_CONVERTER.convert(clusterNodesLine);
return ClusterNodesConverter.INSTANCE.convert(clusterNodesLine);
}
/**
@@ -338,8 +222,7 @@ abstract public class Converters {
* @return
* @since 1.8
*/
public static Converter<Long, Long> secondsToTimeUnit(final TimeUnit timeUnit) {
public static Converter<Long, Long> secondsToTimeUnit(TimeUnit timeUnit) {
return seconds -> secondsToTimeUnit(seconds, timeUnit);
}
@@ -365,12 +248,11 @@ abstract public class Converters {
/**
* Creates a new {@link Converter} to convert from milliseconds to the given {@link TimeUnit}.
*
* @param timeUnit muist not be {@literal null}.
* @param timeUnit must not be {@literal null}.
* @return
* @since 1.8
*/
public static Converter<Long, Long> millisecondsToTimeUnit(final TimeUnit timeUnit) {
public static Converter<Long, Long> millisecondsToTimeUnit(TimeUnit timeUnit) {
return seconds -> millisecondsToTimeUnit(seconds, timeUnit);
}
@@ -406,7 +288,18 @@ abstract public class Converters {
* @since 2.0
*/
public static Properties toProperties(List<String> input) {
return STRING_LIST_TO_PROPERTIES_CONVERTER.convert(input);
Assert.notNull(input, "Input list must not be null!");
Assert.isTrue(input.size() % 2 == 0, "Input list must contain an even number of entries!");
Properties properties = new Properties();
for (int i = 0; i < input.size(); i += 2) {
properties.setProperty(input.get(i), input.get(i + 1));
}
return properties;
}
/**
@@ -417,7 +310,7 @@ abstract public class Converters {
* @since 2.0
*/
public static Converter<List<String>, Properties> listToPropertiesConverter() {
return STRING_LIST_TO_PROPERTIES_CONVERTER;
return Converters::toProperties;
}
/**
@@ -428,7 +321,7 @@ abstract public class Converters {
*/
@SuppressWarnings("unchecked")
public static <K, V> Converter<Map<K, V>, Properties> mapToPropertiesConverter() {
return (Converter) MAP_TO_PROPERTIES;
return (Converter) MapToPropertiesConverter.INSTANCE;
}
/**
@@ -553,21 +446,19 @@ abstract public class Converters {
* @return never {@literal null}.
*/
DistanceConverter forMetric(@Nullable Metric metric) {
return new DistanceConverter(
metric == null || ObjectUtils.nullSafeEquals(Metrics.NEUTRAL, metric) ? DistanceUnit.METERS : metric);
return new DistanceConverter(ObjectUtils.nullSafeEquals(Metrics.NEUTRAL, metric) ? DistanceUnit.METERS : metric);
}
static class DistanceConverter implements Converter<Double, Distance> {
private Metric metric;
private final Metric metric;
/**
* @param metric can be {@literal null}. Defaults to {@link DistanceUnit#METERS}.
* @return never {@literal null}.
*/
DistanceConverter(@Nullable Metric metric) {
this.metric = metric == null || ObjectUtils.nullSafeEquals(Metrics.NEUTRAL, metric) ? DistanceUnit.METERS
: metric;
DistanceConverter(Metric metric) {
this.metric = ObjectUtils.nullSafeEquals(Metrics.NEUTRAL, metric) ? DistanceUnit.METERS : metric;
}
/*
@@ -613,4 +504,110 @@ abstract public class Converters {
return new GeoResults<>(values, source.getAverageDistance().getMetric());
}
}
enum ClusterNodesConverter implements Converter<String, RedisClusterNode> {
INSTANCE;
private static final Map<String, Flag> flagLookupMap;
static {
flagLookupMap = new LinkedHashMap<>(Flag.values().length, 1);
for (Flag flag : Flag.values()) {
flagLookupMap.put(flag.getRaw(), flag);
}
}
static final int ID_INDEX = 0;
static final int HOST_PORT_INDEX = 1;
static final int FLAGS_INDEX = 2;
static final int MASTER_ID_INDEX = 3;
static final int LINK_STATE_INDEX = 7;
static final int SLOTS_INDEX = 8;
public RedisClusterNode convert(String source) {
String[] args = source.split(" ");
String[] hostAndPort = StringUtils.split(args[HOST_PORT_INDEX], ":");
Assert.notNull(hostAndPort, "ClusterNode information does not define host and port!");
SlotRange range = parseSlotRange(args);
Set<Flag> flags = parseFlags(args);
String portPart = hostAndPort[1];
if (portPart.contains("@")) {
portPart = portPart.substring(0, portPart.indexOf('@'));
}
RedisClusterNodeBuilder nodeBuilder = RedisClusterNode.newRedisClusterNode()
.listeningAt(hostAndPort[0], Integer.valueOf(portPart)) //
.withId(args[ID_INDEX]) //
.promotedAs(flags.contains(Flag.MASTER) ? NodeType.MASTER : NodeType.SLAVE) //
.serving(range) //
.withFlags(flags) //
.linkState(parseLinkState(args));
if (!args[MASTER_ID_INDEX].isEmpty() && !args[MASTER_ID_INDEX].startsWith("-")) {
nodeBuilder.slaveOf(args[MASTER_ID_INDEX]);
}
return nodeBuilder.build();
}
private Set<Flag> parseFlags(String[] args) {
String raw = args[FLAGS_INDEX];
Set<Flag> flags = new LinkedHashSet<>(8, 1);
if (StringUtils.hasText(raw)) {
for (String flag : raw.split(",")) {
flags.add(flagLookupMap.get(flag));
}
}
return flags;
}
private LinkState parseLinkState(String[] args) {
String raw = args[LINK_STATE_INDEX];
if (StringUtils.hasText(raw)) {
return LinkState.valueOf(raw.toUpperCase());
}
return LinkState.DISCONNECTED;
}
private SlotRange parseSlotRange(String[] args) {
Set<Integer> slots = new LinkedHashSet<>();
for (int i = SLOTS_INDEX; i < args.length; i++) {
String raw = args[i];
if (raw.startsWith("[")) {
continue;
}
if (raw.contains("-")) {
String[] slotRange = StringUtils.split(raw, "-");
if (slotRange != null) {
int from = Integer.valueOf(slotRange[0]);
int to = Integer.valueOf(slotRange[1]);
for (int slot = from; slot <= to; slot++) {
slots.add(slot);
}
}
} else {
slots.add(Integer.valueOf(raw));
}
}
return new SlotRange(slots);
}
}
}

View File

@@ -21,6 +21,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
/**
@@ -33,7 +34,7 @@ import org.springframework.util.Assert;
*/
public class SetConverter<S, T> implements Converter<Set<S>, Set<T>> {
private Converter<S, T> itemConverter;
private final Converter<S, T> itemConverter;
/**
* @param itemConverter The {@link Converter} to use for converting individual Set items. Must not be {@literal null}.
@@ -49,6 +50,7 @@ public class SetConverter<S, T> implements Converter<Set<S>, Set<T>> {
* @see org.springframework.core.convert.converter.Converter#convert(Object)
*/
@Override
@NonNull
public Set<T> convert(Set<S> source) {
return source.stream().map(itemConverter::convert)

View File

@@ -21,6 +21,7 @@ import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.data.redis.core.types.RedisClientInfo.RedisClientInfoBuilder;
import org.springframework.lang.NonNull;
/**
* {@link Converter} implementation to create one {@link RedisClientInfo} per line entry in given {@link String} array.
@@ -42,6 +43,7 @@ public class StringToRedisClientInfoConverter implements Converter<String[], Lis
* @see org.springframework.core.convert.converter.Converter#convert(Object)
*/
@Override
@NonNull
public List<RedisClientInfo> convert(String[] lines) {
List<RedisClientInfo> clientInfoList = new ArrayList<>(lines.length);

View File

@@ -24,6 +24,7 @@ import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.connection.convert.SetConverter;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanCursor;
import org.springframework.data.redis.core.ScanIteration;
@@ -40,6 +41,8 @@ import org.springframework.util.Assert;
*/
class JedisClusterZSetCommands implements RedisZSetCommands {
private static final SetConverter<redis.clients.jedis.Tuple, Tuple> TUPLE_SET_CONVERTER = new SetConverter<>(
JedisConverters::toTuple);
private final JedisClusterConnection connection;
JedisClusterZSetCommands(JedisClusterConnection connection) {
@@ -181,9 +184,9 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
try {
if (limit.isUnlimited()) {
return JedisConverters.toTupleSet(connection.getCluster().zrangeByScoreWithScores(key, min, max));
return toTupleSet(connection.getCluster().zrangeByScoreWithScores(key, min, max));
}
return JedisConverters.toTupleSet(
return toTupleSet(
connection.getCluster().zrangeByScoreWithScores(key, min, max, limit.getOffset(), limit.getCount()));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
@@ -228,9 +231,9 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
try {
if (limit.isUnlimited()) {
return JedisConverters.toTupleSet(connection.getCluster().zrevrangeByScoreWithScores(key, max, min));
return toTupleSet(connection.getCluster().zrevrangeByScoreWithScores(key, max, min));
}
return JedisConverters.toTupleSet(
return toTupleSet(
connection.getCluster().zrevrangeByScoreWithScores(key, max, min, limit.getOffset(), limit.getCount()));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
@@ -379,7 +382,7 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
Assert.notNull(key, "Key must not be null!");
try {
return JedisConverters.toTupleSet(connection.getCluster().zrangeWithScores(key, start, end));
return toTupleSet(connection.getCluster().zrangeWithScores(key, start, end));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
@@ -411,7 +414,7 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
Assert.notNull(key, "Key must not be null!");
try {
return JedisConverters.toTupleSet(connection.getCluster().zrangeByScoreWithScores(key, min, max));
return toTupleSet(connection.getCluster().zrangeByScoreWithScores(key, min, max));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
@@ -452,7 +455,7 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
}
try {
return JedisConverters.toTupleSet(connection.getCluster().zrangeByScoreWithScores(key, min, max,
return toTupleSet(connection.getCluster().zrangeByScoreWithScores(key, min, max,
Long.valueOf(offset).intValue(), Long.valueOf(count).intValue()));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
@@ -485,7 +488,7 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
Assert.notNull(key, "Key must not be null!");
try {
return JedisConverters.toTupleSet(connection.getCluster().zrevrangeWithScores(key, start, end));
return toTupleSet(connection.getCluster().zrevrangeWithScores(key, start, end));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
@@ -517,7 +520,7 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
Assert.notNull(key, "Key must not be null!");
try {
return JedisConverters.toTupleSet(connection.getCluster().zrevrangeByScoreWithScores(key, max, min));
return toTupleSet(connection.getCluster().zrevrangeByScoreWithScores(key, max, min));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
@@ -558,7 +561,7 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
}
try {
return JedisConverters.toTupleSet(connection.getCluster().zrevrangeByScoreWithScores(key, max, min,
return toTupleSet(connection.getCluster().zrevrangeByScoreWithScores(key, max, min,
Long.valueOf(offset).intValue(), Long.valueOf(count).intValue()));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
@@ -819,4 +822,8 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
return connection.convertJedisAccessException(ex);
}
private static Set<Tuple> toTupleSet(Set<redis.clients.jedis.Tuple> source) {
return TUPLE_SET_CONVERTER.convert(source);
}
}

View File

@@ -28,6 +28,7 @@ import redis.clients.jedis.util.SafeEncoder;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -56,7 +57,6 @@ import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusComma
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.Flag;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.connection.RedisServer;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.RedisStringCommands.BitOperation;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.connection.RedisZSetCommands.Range.Boundary;
@@ -75,7 +75,6 @@ import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -90,163 +89,25 @@ import org.springframework.util.StringUtils;
* @author Ninad Divadkar
* @author Guy Korland
*/
abstract public class JedisConverters extends Converters {
public abstract class JedisConverters extends Converters {
private static final Converter<String, byte[]> STRING_TO_BYTES;
private static final ListConverter<String, byte[]> STRING_LIST_TO_BYTE_LIST;
private static final SetConverter<String, byte[]> STRING_SET_TO_BYTE_SET;
private static final MapConverter<String, byte[]> STRING_MAP_TO_BYTE_MAP;
private static final SetConverter<redis.clients.jedis.Tuple, Tuple> TUPLE_SET_TO_TUPLE_SET;
private static final Converter<Exception, DataAccessException> EXCEPTION_CONVERTER = new JedisExceptionConverter();
private static final Converter<String[], List<RedisClientInfo>> STRING_TO_CLIENT_INFO_CONVERTER = new StringToRedisClientInfoConverter();
private static final Converter<redis.clients.jedis.Tuple, Tuple> TUPLE_CONVERTER;
private static final ListConverter<redis.clients.jedis.Tuple, Tuple> TUPLE_LIST_TO_TUPLE_LIST_CONVERTER;
private static final Converter<Object, RedisClusterNode> OBJECT_TO_CLUSTER_NODE_CONVERTER;
private static final Converter<Expiration, byte[]> EXPIRATION_TO_COMMAND_OPTION_CONVERTER;
private static final Converter<SetOption, byte[]> SET_OPTION_TO_COMMAND_OPTION_CONVERTER;
private static final Converter<List<String>, Long> STRING_LIST_TO_TIME_CONVERTER;
private static final Converter<redis.clients.jedis.GeoCoordinate, Point> GEO_COORDINATE_TO_POINT_CONVERTER;
private static final ListConverter<redis.clients.jedis.GeoCoordinate, Point> LIST_GEO_COORDINATE_TO_POINT_CONVERTER;
private static final Converter<byte[], String> BYTES_TO_STRING_CONVERTER;
private static final ListConverter<byte[], String> BYTES_LIST_TO_STRING_LIST_CONVERTER;
private static final ListConverter<byte[], Long> BYTES_LIST_TO_LONG_LIST_CONVERTER;
private static final Converter<BitFieldSubCommands, List<byte[]>> BITFIELD_COMMAND_ARGUMENT_CONVERTER;
public static final byte[] PLUS_BYTES;
public static final byte[] MINUS_BYTES;
public static final byte[] POSITIVE_INFINITY_BYTES;
public static final byte[] NEGATIVE_INFINITY_BYTES;
private static final byte[] EX;
private static final byte[] PX;
private static final byte[] NX;
private static final byte[] XX;
static {
BYTES_TO_STRING_CONVERTER = source -> source == null ? null : SafeEncoder.encode(source);
BYTES_LIST_TO_STRING_LIST_CONVERTER = new ListConverter<>(BYTES_TO_STRING_CONVERTER);
STRING_TO_BYTES = source -> source == null ? null : SafeEncoder.encode(source);
STRING_LIST_TO_BYTE_LIST = new ListConverter<>(STRING_TO_BYTES);
STRING_SET_TO_BYTE_SET = new SetConverter<>(STRING_TO_BYTES);
STRING_MAP_TO_BYTE_MAP = new MapConverter<>(STRING_TO_BYTES);
TUPLE_CONVERTER = source -> source != null ? new DefaultTuple(source.getBinaryElement(), source.getScore()) : null;
TUPLE_SET_TO_TUPLE_SET = new SetConverter<>(TUPLE_CONVERTER);
TUPLE_LIST_TO_TUPLE_LIST_CONVERTER = new ListConverter<>(TUPLE_CONVERTER);
PLUS_BYTES = toBytes("+");
MINUS_BYTES = toBytes("-");
POSITIVE_INFINITY_BYTES = toBytes("+inf");
NEGATIVE_INFINITY_BYTES = toBytes("-inf");
OBJECT_TO_CLUSTER_NODE_CONVERTER = infos -> {
List<Object> values = (List<Object>) infos;
RedisClusterNode.SlotRange range = new RedisClusterNode.SlotRange(((Number) values.get(0)).intValue(),
((Number) values.get(1)).intValue());
List<Object> nodeInfo = (List<Object>) values.get(2);
return new RedisClusterNode(toString((byte[]) nodeInfo.get(0)), ((Number) nodeInfo.get(1)).intValue(), range);
};
EX = toBytes("EX");
PX = toBytes("PX");
EXPIRATION_TO_COMMAND_OPTION_CONVERTER = new Converter<Expiration, byte[]>() {
@Override
public byte[] convert(Expiration source) {
if (source == null || source.isPersistent()) {
return new byte[0];
}
if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, source.getTimeUnit())) {
return PX;
}
return EX;
}
};
NX = toBytes("NX");
XX = toBytes("XX");
SET_OPTION_TO_COMMAND_OPTION_CONVERTER = new Converter<RedisStringCommands.SetOption, byte[]>() {
@Override
public byte[] convert(SetOption source) {
switch (source) {
case UPSERT:
return new byte[0];
case SET_IF_ABSENT:
return NX;
case SET_IF_PRESENT:
return XX;
}
throw new IllegalArgumentException(String.format("Invalid argument %s for SetOption.", source));
}
};
STRING_LIST_TO_TIME_CONVERTER = source -> {
Assert.notEmpty(source, "Received invalid result from server. Expected 2 items in collection.");
Assert.isTrue(source.size() == 2,
"Received invalid nr of arguments from redis server. Expected 2 received " + source.size());
return toTimeMillis(source.get(0), source.get(1));
};
GEO_COORDINATE_TO_POINT_CONVERTER = geoCoordinate -> geoCoordinate != null
? new Point(geoCoordinate.getLongitude(), geoCoordinate.getLatitude())
: null;
LIST_GEO_COORDINATE_TO_POINT_CONVERTER = new ListConverter<>(GEO_COORDINATE_TO_POINT_CONVERTER);
BYTES_LIST_TO_LONG_LIST_CONVERTER = new ListConverter<byte[], Long>(new Converter<byte[], Long>() {
@Override
public Long convert(byte[] source) {
return Long.valueOf(JedisConverters.toString(source));
}
});
BITFIELD_COMMAND_ARGUMENT_CONVERTER = new Converter<BitFieldSubCommands, List<byte[]>>() {
@Override
public List<byte[]> convert(BitFieldSubCommands source) {
if (source == null) {
return Collections.emptyList();
}
List<byte[]> args = new ArrayList<byte[]>(source.getSubCommands().size() * 4);
for (BitFieldSubCommand command : source.getSubCommands()) {
if (command instanceof BitFieldIncrBy) {
BitFieldIncrBy.Overflow overflow = ((BitFieldIncrBy) command).getOverflow();
if (overflow != null) {
args.add(JedisConverters.toBytes("OVERFLOW"));
args.add(JedisConverters.toBytes(overflow.name()));
}
}
args.add(JedisConverters.toBytes(command.getCommand()));
args.add(JedisConverters.toBytes(command.getType().asString()));
args.add(JedisConverters.toBytes(command.getOffset().asString()));
if (command instanceof BitFieldSet) {
args.add(JedisConverters.toBytes(((BitFieldSet) command).getValue()));
} else if (command instanceof BitFieldIncrBy) {
args.add(JedisConverters.toBytes(((BitFieldIncrBy) command).getValue()));
}
}
return args;
}
};
}
public static Converter<String, byte[]> stringToBytes() {
return STRING_TO_BYTES;
return JedisConverters::toBytes;
}
/**
@@ -256,23 +117,35 @@ abstract public class JedisConverters extends Converters {
* @since 1.4
*/
public static ListConverter<redis.clients.jedis.Tuple, Tuple> tuplesToTuples() {
return TUPLE_LIST_TO_TUPLE_LIST_CONVERTER;
return new ListConverter<>(JedisConverters::toTuple);
}
public static ListConverter<String, byte[]> stringListToByteList() {
return STRING_LIST_TO_BYTE_LIST;
return new ListConverter<>(stringToBytes());
}
/**
* @deprecated since 2.5
*/
@Deprecated
public static SetConverter<String, byte[]> stringSetToByteSet() {
return STRING_SET_TO_BYTE_SET;
return new SetConverter<>(stringToBytes());
}
/**
* @deprecated since 2.5
*/
@Deprecated
public static MapConverter<String, byte[]> stringMapToByteMap() {
return STRING_MAP_TO_BYTE_MAP;
return new MapConverter<>(stringToBytes());
}
/**
* @deprecated since 2.5
*/
@Deprecated
public static SetConverter<redis.clients.jedis.Tuple, Tuple> tupleSetToTupleSet() {
return TUPLE_SET_TO_TUPLE_SET;
return new SetConverter<>(JedisConverters::toTuple);
}
public static Converter<Exception, DataAccessException> exceptionConverter() {
@@ -287,8 +160,16 @@ abstract public class JedisConverters extends Converters {
return result;
}
/**
* @deprecated since 2.5
*/
@Deprecated
public static Set<Tuple> toTupleSet(Set<redis.clients.jedis.Tuple> source) {
return TUPLE_SET_TO_TUPLE_SET.convert(source);
return tupleSetToTupleSet().convert(source);
}
public static Tuple toTuple(redis.clients.jedis.Tuple source) {
return new DefaultTuple(source.getBinaryElement(), source.getScore());
}
/**
@@ -340,8 +221,9 @@ abstract public class JedisConverters extends Converters {
return toBytes(String.valueOf(source));
}
public static byte[] toBytes(String source) {
return STRING_TO_BYTES.convert(source);
@Nullable
public static byte[] toBytes(@Nullable String source) {
return source == null ? null : SafeEncoder.encode(source);
}
@Nullable
@@ -349,6 +231,10 @@ abstract public class JedisConverters extends Converters {
return source == null ? null : SafeEncoder.encode(source);
}
public static Long toLong(byte[] source) {
return Long.valueOf(toString(source));
}
/**
* Convert the given {@code source} value to the corresponding {@link ValueEncoding}.
*
@@ -356,7 +242,7 @@ abstract public class JedisConverters extends Converters {
* @return the {@link ValueEncoding} for given {@code source}. Never {@literal null}.
* @since 2.1
*/
public static ValueEncoding toEncoding(@Nullable byte[] source) {
public static ValueEncoding toEncoding(byte[] source) {
return ValueEncoding.of(toString(source));
}
@@ -365,8 +251,15 @@ abstract public class JedisConverters extends Converters {
* @return
* @since 1.7
*/
@SuppressWarnings("unchecked")
public static RedisClusterNode toNode(Object source) {
return OBJECT_TO_CLUSTER_NODE_CONVERTER.convert(source);
List<Object> values = (List<Object>) source;
RedisClusterNode.SlotRange range = new RedisClusterNode.SlotRange(((Number) values.get(0)).intValue(),
((Number) values.get(1)).intValue());
List<Object> nodeInfo = (List<Object>) values.get(2);
return new RedisClusterNode(toString((byte[]) nodeInfo.get(0)), ((Number) nodeInfo.get(1)).intValue(), range);
}
/**
@@ -379,7 +272,8 @@ abstract public class JedisConverters extends Converters {
if (!StringUtils.hasText(source)) {
return Collections.emptyList();
}
return STRING_TO_CLIENT_INFO_CONVERTER.convert(source.split("\\r?\\n"));
return StringToRedisClientInfoConverter.INSTANCE.convert(source.split("\\r?\\n"));
}
/**
@@ -389,17 +283,13 @@ abstract public class JedisConverters extends Converters {
*/
public static List<RedisServer> toListOfRedisServer(List<Map<String, String>> source) {
if (CollectionUtils.isEmpty(source)) {
return Collections.emptyList();
}
List<RedisServer> sentinels = new ArrayList<>();
for (Map<String, String> info : source) {
sentinels.add(RedisServer.newServerFrom(Converters.toProperties(info)));
}
return sentinels;
return toList(it -> RedisServer.newServerFrom(Converters.toProperties(it)), source);
}
/**
* @deprecated since 2.5
*/
@Deprecated
public static DataAccessException toDataAccessException(Exception ex) {
return EXCEPTION_CONVERTER.convert(ex);
}
@@ -657,8 +547,13 @@ abstract public class JedisConverters extends Converters {
return sp;
}
static Converter<List<String>, Long> toTimeConverter() {
return STRING_LIST_TO_TIME_CONVERTER;
static Long toTime(List<String> source) {
Assert.notEmpty(source, "Received invalid result from server. Expected 2 items in collection.");
Assert.isTrue(source.size() == 2,
"Received invalid nr of arguments from redis server. Expected 2 received " + source.size());
return toTimeMillis(source.get(0), source.get(1));
}
/**
@@ -667,26 +562,67 @@ abstract public class JedisConverters extends Converters {
* @since 1.8
*/
public static List<String> toStrings(List<byte[]> source) {
return BYTES_LIST_TO_STRING_LIST_CONVERTER.convert(source);
return toList(JedisConverters::toString, source);
}
private static <S, T> List<T> toList(Converter<S, T> converter, @Nullable Collection<S> source) {
if (source == null || source.isEmpty()) {
return Collections.emptyList();
}
List<T> target = new ArrayList<>(source.size());
for (S s : source) {
target.add(converter.convert(s));
}
return target;
}
/**
* @return
* @deprecated since 2.5
*/
@Deprecated
public static ListConverter<byte[], String> bytesListToStringListConverter() {
return BYTES_LIST_TO_STRING_LIST_CONVERTER;
return new ListConverter<>(JedisConverters::toString);
}
/**
* @deprecated since 2.5
*/
@Deprecated
public static ListConverter<byte[], Long> getBytesListToLongListConverter() {
return BYTES_LIST_TO_LONG_LIST_CONVERTER;
return new ListConverter<>(JedisConverters::toLong);
}
/**
* @return
* @since 1.8
* @deprecated since 2.5
*/
public static ListConverter<redis.clients.jedis.GeoCoordinate, Point> geoCoordinateToPointConverter() {
return LIST_GEO_COORDINATE_TO_POINT_CONVERTER;
return new ListConverter<>(JedisConverters::toPoint);
}
/**
* @return
* @since 2.5
*/
@Nullable
static Point toPoint(@Nullable redis.clients.jedis.GeoCoordinate geoCoordinate) {
return geoCoordinate == null ? null : new Point(geoCoordinate.getLongitude(), geoCoordinate.getLatitude());
}
/**
* Convert {@link Point} into {@link GeoCoordinate}.
*
* @param source
* @return
* @since 1.8
*/
public static GeoCoordinate toGeoCoordinate(Point source) {
return new redis.clients.jedis.GeoCoordinate(source.getX(), source.getY());
}
/**
@@ -715,16 +651,6 @@ abstract public class JedisConverters extends Converters {
return ObjectUtils.caseInsensitiveValueOf(GeoUnit.values(), metricToUse.getAbbreviation());
}
/**
* Convert {@link Point} into {@link GeoCoordinate}.
*
* @param source
* @return
* @since 1.8
*/
public static GeoCoordinate toGeoCoordinate(Point source) {
return source == null ? null : new redis.clients.jedis.GeoCoordinate(source.getX(), source.getY());
}
/**
* Convert {@link GeoRadiusCommandArgs} into {@link GeoRadiusParam}.
@@ -778,10 +704,33 @@ abstract public class JedisConverters extends Converters {
* @return never {@literal null}.
* @since 1.8
*/
public static byte[][] toBitfieldCommandArguments(BitFieldSubCommands bitfieldOperation) {
public static byte[][] toBitfieldCommandArguments(BitFieldSubCommands source) {
List<byte[]> tmp = BITFIELD_COMMAND_ARGUMENT_CONVERTER.convert(bitfieldOperation);
return tmp.toArray(new byte[tmp.size()][]);
List<byte[]> args = new ArrayList<>(source.getSubCommands().size() * 4);
for (BitFieldSubCommand command : source.getSubCommands()) {
if (command instanceof BitFieldIncrBy) {
BitFieldIncrBy.Overflow overflow = ((BitFieldIncrBy) command).getOverflow();
if (overflow != null) {
args.add(JedisConverters.toBytes("OVERFLOW"));
args.add(JedisConverters.toBytes(overflow.name()));
}
}
args.add(JedisConverters.toBytes(command.getCommand()));
args.add(JedisConverters.toBytes(command.getType().asString()));
args.add(JedisConverters.toBytes(command.getOffset().asString()));
if (command instanceof BitFieldSet) {
args.add(JedisConverters.toBytes(((BitFieldSet) command).getValue()));
} else if (command instanceof BitFieldIncrBy) {
args.add(JedisConverters.toBytes(((BitFieldIncrBy) command).getValue()));
}
}
return args.toArray(new byte[0][0]);
}
/**
@@ -794,13 +743,13 @@ abstract public class JedisConverters extends Converters {
Converter<List<redis.clients.jedis.GeoRadiusResponse>, GeoResults<GeoLocation<byte[]>>> forMetric(Metric metric) {
return new GeoResultsConverter(
metric == null || ObjectUtils.nullSafeEquals(Metrics.NEUTRAL, metric) ? DistanceUnit.METERS : metric);
ObjectUtils.nullSafeEquals(Metrics.NEUTRAL, metric) ? DistanceUnit.METERS : metric);
}
private static class GeoResultsConverter
implements Converter<List<redis.clients.jedis.GeoRadiusResponse>, GeoResults<GeoLocation<byte[]>>> {
private Metric metric;
private final Metric metric;
public GeoResultsConverter(Metric metric) {
this.metric = metric;
@@ -837,7 +786,7 @@ abstract public class JedisConverters extends Converters {
private static class GeoResultConverter
implements Converter<redis.clients.jedis.GeoRadiusResponse, GeoResult<GeoLocation<byte[]>>> {
private Metric metric;
private final Metric metric;
public GeoResultConverter(Metric metric) {
this.metric = metric;
@@ -846,7 +795,7 @@ abstract public class JedisConverters extends Converters {
@Override
public GeoResult<GeoLocation<byte[]>> convert(redis.clients.jedis.GeoRadiusResponse source) {
Point point = GEO_COORDINATE_TO_POINT_CONVERTER.convert(source.getCoordinate());
Point point = JedisConverters.toPoint(source.getCoordinate());
return new GeoResult<>(new GeoLocation<>(source.getMember(), point),
new Distance(source.getDistance(), metric));

View File

@@ -31,7 +31,6 @@ import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.convert.ListConverter;
import org.springframework.util.Assert;
/**
@@ -147,8 +146,8 @@ class JedisGeoCommands implements RedisGeoCommands {
Assert.notNull(members, "Members must not be null!");
Assert.noNullElements(members, "Members must not contain null!");
return connection.invoke().from(BinaryJedis::geohash, MultiKeyPipelineBase::geohash, key, members)
.get(JedisConverters.bytesListToStringListConverter());
return connection.invoke().fromMany(BinaryJedis::geohash, MultiKeyPipelineBase::geohash, key, members)
.toList(JedisConverters::toString);
}
/*
@@ -162,9 +161,9 @@ class JedisGeoCommands implements RedisGeoCommands {
Assert.notNull(members, "Members must not be null!");
Assert.noNullElements(members, "Members must not contain null!");
ListConverter<GeoCoordinate, Point> converter = JedisConverters.geoCoordinateToPointConverter();
return connection.invoke().from(BinaryJedis::geopos, MultiKeyPipelineBase::geopos, key, members).get(converter);
return connection.invoke().fromMany(BinaryJedis::geopos, MultiKeyPipelineBase::geopos, key, members)
.toList(JedisConverters::toPoint);
}
/*

View File

@@ -195,7 +195,7 @@ class JedisServerCommands implements RedisServerCommands {
@Override
public Long time() {
return connection.invoke().from(BinaryJedis::time, MultiKeyPipelineBase::time)
.get(JedisConverters.toTimeConverter());
.get(JedisConverters::toTime);
}
/*

View File

@@ -150,8 +150,8 @@ class JedisZSetCommands implements RedisZSetCommands {
Assert.notNull(key, "Key must not be null!");
return connection.invoke()
.from(BinaryJedis::zrangeWithScores, MultiKeyPipelineBase::zrangeWithScores, key, start, end)
.get(JedisConverters.tupleSetToTupleSet());
.fromMany(BinaryJedis::zrangeWithScores, MultiKeyPipelineBase::zrangeWithScores, key, start, end)
.toSet(JedisConverters::toTuple);
}
/*
@@ -169,14 +169,14 @@ class JedisZSetCommands implements RedisZSetCommands {
byte[] max = JedisConverters.boundaryToBytesForZRange(range.getMax(), JedisConverters.POSITIVE_INFINITY_BYTES);
if (!limit.isUnlimited()) {
return connection.invoke().from(BinaryJedis::zrangeByScoreWithScores,
return connection.invoke().fromMany(BinaryJedis::zrangeByScoreWithScores,
MultiKeyPipelineBase::zrangeByScoreWithScores, key, min, max, limit.getOffset(), limit.getCount())
.get(JedisConverters.tupleSetToTupleSet());
.toSet(JedisConverters::toTuple);
}
return connection.invoke()
.from(BinaryJedis::zrangeByScoreWithScores, MultiKeyPipelineBase::zrangeByScoreWithScores, key, min, max)
.get(JedisConverters.tupleSetToTupleSet());
.fromMany(BinaryJedis::zrangeByScoreWithScores, MultiKeyPipelineBase::zrangeByScoreWithScores, key, min, max)
.toSet(JedisConverters::toTuple);
}
/*
@@ -201,8 +201,8 @@ class JedisZSetCommands implements RedisZSetCommands {
Assert.notNull(key, "Key must not be null!");
return connection.invoke()
.from(BinaryJedis::zrevrangeWithScores, MultiKeyPipelineBase::zrevrangeWithScores, key, start, end)
.get(JedisConverters.tupleSetToTupleSet());
.fromMany(BinaryJedis::zrevrangeWithScores, MultiKeyPipelineBase::zrevrangeWithScores, key, start, end)
.toSet(JedisConverters::toTuple);
}
/*
@@ -243,14 +243,15 @@ class JedisZSetCommands implements RedisZSetCommands {
byte[] max = JedisConverters.boundaryToBytesForZRange(range.getMax(), JedisConverters.POSITIVE_INFINITY_BYTES);
if (!limit.isUnlimited()) {
return connection.invoke().from(BinaryJedis::zrevrangeByScoreWithScores,
return connection.invoke().fromMany(BinaryJedis::zrevrangeByScoreWithScores,
MultiKeyPipelineBase::zrevrangeByScoreWithScores, key, max, min, limit.getOffset(), limit.getCount())
.get(JedisConverters.tupleSetToTupleSet());
.toSet(JedisConverters::toTuple);
}
return connection.invoke()
.from(BinaryJedis::zrevrangeByScoreWithScores, MultiKeyPipelineBase::zrevrangeByScoreWithScores, key, max, min)
.get(JedisConverters.tupleSetToTupleSet());
.fromMany(BinaryJedis::zrevrangeByScoreWithScores, MultiKeyPipelineBase::zrevrangeByScoreWithScores, key, max,
min)
.toSet(JedisConverters::toTuple);
}
/*
@@ -474,8 +475,8 @@ class JedisZSetCommands implements RedisZSetCommands {
Assert.notNull(key, "Key must not be null!");
String keyStr = new String(key, StandardCharsets.UTF_8);
return connection.invoke().from(Jedis::zrangeByScore, MultiKeyPipelineBase::zrangeByScore, keyStr, min, max)
.get(JedisConverters.stringSetToByteSet());
return connection.invoke().fromMany(Jedis::zrangeByScore, MultiKeyPipelineBase::zrangeByScore, keyStr, min, max)
.toSet(JedisConverters::toBytes);
}
/*
@@ -495,8 +496,9 @@ class JedisZSetCommands implements RedisZSetCommands {
String keyStr = new String(key, StandardCharsets.UTF_8);
return connection.invoke()
.from(Jedis::zrangeByScore, MultiKeyPipelineBase::zrangeByScore, keyStr, min, max, (int) offset, (int) count)
.get(JedisConverters.stringSetToByteSet());
.fromMany(Jedis::zrangeByScore, MultiKeyPipelineBase::zrangeByScore, keyStr, min, max, (int) offset,
(int) count)
.toSet(JedisConverters::toBytes);
}
/*