diff --git a/src/main/java/org/springframework/data/redis/connection/convert/Converters.java b/src/main/java/org/springframework/data/redis/connection/convert/Converters.java index 7e9f024e8..bd7e41994 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/Converters.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/Converters.java @@ -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_TO_PROPS = new StringToPropertiesConverter(); - private static final Converter LONG_TO_BOOLEAN = new LongToBooleanConverter(); - private static final Converter STRING_TO_DATA_TYPE = new StringToDataTypeConverter(); - private static final Converter, Properties> MAP_TO_PROPERTIES = MapToPropertiesConverter.INSTANCE; - private static final Converter STRING_TO_CLUSTER_NODE_CONVERTER; - private static final Converter, Properties> STRING_LIST_TO_PROPERTIES_CONVERTER; - private static final Map 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() { - - 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 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 parseFlags(String[] args) { - - String raw = args[FLAGS_INDEX]; - - Set 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 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 stringToBooleanConverter() { - return (source) -> ObjectUtils.nullSafeEquals("OK", source); + return Converters::stringToBoolean; } public static Converter stringToProps() { - return STRING_TO_PROPS; + return Converters::toProperties; } public static Converter longToBoolean() { - return LONG_TO_BOOLEAN; + return Converters::toBoolean; } public static Converter 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 secondsToTimeUnit(final TimeUnit timeUnit) { - + public static Converter 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 millisecondsToTimeUnit(final TimeUnit timeUnit) { - + public static Converter millisecondsToTimeUnit(TimeUnit timeUnit) { return seconds -> millisecondsToTimeUnit(seconds, timeUnit); } @@ -406,7 +288,18 @@ abstract public class Converters { * @since 2.0 */ public static Properties toProperties(List 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, Properties> listToPropertiesConverter() { - return STRING_LIST_TO_PROPERTIES_CONVERTER; + return Converters::toProperties; } /** @@ -428,7 +321,7 @@ abstract public class Converters { */ @SuppressWarnings("unchecked") public static Converter, 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 { - 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 { + + INSTANCE; + + private static final Map 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 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 parseFlags(String[] args) { + + String raw = args[FLAGS_INDEX]; + + Set 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 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); + } + + } } diff --git a/src/main/java/org/springframework/data/redis/connection/convert/SetConverter.java b/src/main/java/org/springframework/data/redis/connection/convert/SetConverter.java index 2907b3d8c..e4a161b7c 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/SetConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/SetConverter.java @@ -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 implements Converter, Set> { - private Converter itemConverter; + private final Converter 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 implements Converter, Set> { * @see org.springframework.core.convert.converter.Converter#convert(Object) */ @Override + @NonNull public Set convert(Set source) { return source.stream().map(itemConverter::convert) diff --git a/src/main/java/org/springframework/data/redis/connection/convert/StringToRedisClientInfoConverter.java b/src/main/java/org/springframework/data/redis/connection/convert/StringToRedisClientInfoConverter.java index 7da91f26d..21ab444c3 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/StringToRedisClientInfoConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/StringToRedisClientInfoConverter.java @@ -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 convert(String[] lines) { List clientInfoList = new ArrayList<>(lines.length); diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java index 27ad51ad0..e64492dbf 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java @@ -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 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 toTupleSet(Set source) { + return TUPLE_SET_CONVERTER.convert(source); + } + } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java index 232d0e921..5e34e143e 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java @@ -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_TO_BYTES; - private static final ListConverter STRING_LIST_TO_BYTE_LIST; - private static final SetConverter STRING_SET_TO_BYTE_SET; - private static final MapConverter STRING_MAP_TO_BYTE_MAP; - private static final SetConverter TUPLE_SET_TO_TUPLE_SET; private static final Converter EXCEPTION_CONVERTER = new JedisExceptionConverter(); - private static final Converter> STRING_TO_CLIENT_INFO_CONVERTER = new StringToRedisClientInfoConverter(); - private static final Converter TUPLE_CONVERTER; - private static final ListConverter TUPLE_LIST_TO_TUPLE_LIST_CONVERTER; - private static final Converter OBJECT_TO_CLUSTER_NODE_CONVERTER; - private static final Converter EXPIRATION_TO_COMMAND_OPTION_CONVERTER; - private static final Converter SET_OPTION_TO_COMMAND_OPTION_CONVERTER; - private static final Converter, Long> STRING_LIST_TO_TIME_CONVERTER; - private static final Converter GEO_COORDINATE_TO_POINT_CONVERTER; - private static final ListConverter LIST_GEO_COORDINATE_TO_POINT_CONVERTER; - private static final Converter BYTES_TO_STRING_CONVERTER; - private static final ListConverter BYTES_LIST_TO_STRING_LIST_CONVERTER; - private static final ListConverter BYTES_LIST_TO_LONG_LIST_CONVERTER; - private static final Converter> 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 values = (List) infos; - RedisClusterNode.SlotRange range = new RedisClusterNode.SlotRange(((Number) values.get(0)).intValue(), - ((Number) values.get(1)).intValue()); - List nodeInfo = (List) 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() { - - @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() { - - @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(new Converter() { - @Override - public Long convert(byte[] source) { - return Long.valueOf(JedisConverters.toString(source)); - } - }); - - BITFIELD_COMMAND_ARGUMENT_CONVERTER = new Converter>() { - @Override - public List convert(BitFieldSubCommands source) { - - if (source == null) { - return Collections.emptyList(); - } - - List 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; - } - }; } public static Converter stringToBytes() { - return STRING_TO_BYTES; + return JedisConverters::toBytes; } /** @@ -256,23 +117,35 @@ abstract public class JedisConverters extends Converters { * @since 1.4 */ public static ListConverter tuplesToTuples() { - return TUPLE_LIST_TO_TUPLE_LIST_CONVERTER; + return new ListConverter<>(JedisConverters::toTuple); } public static ListConverter stringListToByteList() { - return STRING_LIST_TO_BYTE_LIST; + return new ListConverter<>(stringToBytes()); } + /** + * @deprecated since 2.5 + */ + @Deprecated public static SetConverter stringSetToByteSet() { - return STRING_SET_TO_BYTE_SET; + return new SetConverter<>(stringToBytes()); } + /** + * @deprecated since 2.5 + */ + @Deprecated public static MapConverter stringMapToByteMap() { - return STRING_MAP_TO_BYTE_MAP; + return new MapConverter<>(stringToBytes()); } + /** + * @deprecated since 2.5 + */ + @Deprecated public static SetConverter tupleSetToTupleSet() { - return TUPLE_SET_TO_TUPLE_SET; + return new SetConverter<>(JedisConverters::toTuple); } public static Converter exceptionConverter() { @@ -287,8 +160,16 @@ abstract public class JedisConverters extends Converters { return result; } + /** + * @deprecated since 2.5 + */ + @Deprecated public static Set toTupleSet(Set 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 values = (List) source; + RedisClusterNode.SlotRange range = new RedisClusterNode.SlotRange(((Number) values.get(0)).intValue(), + ((Number) values.get(1)).intValue()); + List nodeInfo = (List) 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 toListOfRedisServer(List> source) { - if (CollectionUtils.isEmpty(source)) { - return Collections.emptyList(); - } - - List sentinels = new ArrayList<>(); - for (Map 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, Long> toTimeConverter() { - return STRING_LIST_TO_TIME_CONVERTER; + static Long toTime(List 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 toStrings(List source) { - return BYTES_LIST_TO_STRING_LIST_CONVERTER.convert(source); + return toList(JedisConverters::toString, source); + } + + private static List toList(Converter converter, @Nullable Collection source) { + + if (source == null || source.isEmpty()) { + return Collections.emptyList(); + } + + List 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 bytesListToStringListConverter() { - return BYTES_LIST_TO_STRING_LIST_CONVERTER; + return new ListConverter<>(JedisConverters::toString); } + /** + * @deprecated since 2.5 + */ + @Deprecated public static ListConverter getBytesListToLongListConverter() { - return BYTES_LIST_TO_LONG_LIST_CONVERTER; + return new ListConverter<>(JedisConverters::toLong); } /** * @return * @since 1.8 + * @deprecated since 2.5 */ public static ListConverter 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 tmp = BITFIELD_COMMAND_ARGUMENT_CONVERTER.convert(bitfieldOperation); - return tmp.toArray(new byte[tmp.size()][]); + List 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, GeoResults>> 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, GeoResults>> { - 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>> { - 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> 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)); diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisGeoCommands.java index f00b24042..fdcee7690 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisGeoCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisGeoCommands.java @@ -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 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); } /* diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisServerCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisServerCommands.java index 08e0f2686..05a410ef1 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisServerCommands.java @@ -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); } /* diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java index 61665d5fa..48c70d506 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java @@ -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); } /*