diff --git a/pom.xml b/pom.xml index c202db66e..594d0d7fd 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ 1.4.8 2.6.0 5.1.3.RELEASE - 3.0.0-m1 + 3.0.0 1.01 4.1.22.Final spring.data.redis diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index 52c16b1f0..6cd6d460d 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -8,6 +8,7 @@ This section briefly covers items that are new and noteworthy in the latest rele * <> * Refined `union`/`diff`/`intersect` set-operation methods accepting a single collection of keys. +* Upgrade to Jedis 3. [[new-in-2.1.0]] == New in Spring Data Redis 2.1 diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientUtils.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientUtils.java index 12d3d1ba5..aac0f92bd 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientUtils.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientUtils.java @@ -18,7 +18,6 @@ package org.springframework.data.redis.connection.jedis; import redis.clients.jedis.BinaryJedis; import redis.clients.jedis.Builder; import redis.clients.jedis.Client; -import redis.clients.jedis.Connection; import redis.clients.jedis.Jedis; import redis.clients.jedis.Protocol; import redis.clients.jedis.Protocol.Command; @@ -35,8 +34,6 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; -import org.springframework.beans.DirectFieldAccessor; -import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; /** @@ -50,7 +47,6 @@ import org.springframework.util.ReflectionUtils; class JedisClientUtils { private static final Field CLIENT_FIELD; - private static final Method SEND_COMMAND; private static final Method GET_RESPONSE; private static final Method PROTOCOL_SEND_COMMAND; private static final Set KNOWN_COMMANDS; @@ -65,20 +61,6 @@ class JedisClientUtils { byte[].class, byte[][].class); ReflectionUtils.makeAccessible(PROTOCOL_SEND_COMMAND); - try { - - Class commandType = ClassUtils.isPresent("redis.clients.jedis.ProtocolCommand", null) - ? ClassUtils.forName("redis.clients.jedis.ProtocolCommand", null) - : ClassUtils.forName("redis.clients.jedis.Protocol$Command", null); - - SEND_COMMAND = ReflectionUtils.findMethod(Connection.class, "sendCommand", commandType, byte[][].class); - } catch (Exception e) { - throw new NoClassDefFoundError( - "Could not find required flavor of command required by 'redis.clients.jedis.Connection#sendCommand'."); - } - - ReflectionUtils.makeAccessible(SEND_COMMAND); - GET_RESPONSE = ReflectionUtils.findMethod(Queable.class, "getResponse", Builder.class); ReflectionUtils.makeAccessible(GET_RESPONSE); @@ -149,26 +131,12 @@ class JedisClientUtils { private static void sendCommand(Client client, String command, byte[][] args) { if (isKnownCommand(command)) { - ReflectionUtils.invokeMethod(SEND_COMMAND, client, Command.valueOf(command.trim().toUpperCase()), args); + client.sendCommand(Command.valueOf(command.trim().toUpperCase()), args); } else { - sendProtocolCommand(client, command, args); + client.sendCommand(() -> SafeEncoder.encode(command.trim().toUpperCase()), args); } } - private static void sendProtocolCommand(Client client, String command, byte[][] args) { - - // quite expensive to construct for each command invocation - DirectFieldAccessor dfa = new DirectFieldAccessor(client); - - client.connect(); - - RedisOutputStream os = (RedisOutputStream) dfa.getPropertyValue("outputStream"); - ReflectionUtils.invokeMethod(PROTOCOL_SEND_COMMAND, null, os, SafeEncoder.encode(command), args); - - Integer pipelinedCommands = (Integer) dfa.getPropertyValue("pipelinedCommands"); - dfa.setPropertyValue("pipelinedCommands", pipelinedCommands + 1); - } - private static boolean isKnownCommand(String command) { return KNOWN_COMMANDS.contains(command); } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterServerCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterServerCommands.java index 898d972f1..fe60edd7d 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterServerCommands.java @@ -335,8 +335,10 @@ class JedisClusterServerCommands implements RedisClusterServerCommands { Assert.notNull(pattern, "Pattern must not be null!"); - return connection.getClusterCommandExecutor().executeCommandOnSingleNode( - (JedisClusterCommandCallback) client -> Converters.toProperties(client.configGet(pattern)), node) + return connection.getClusterCommandExecutor() + .executeCommandOnSingleNode( + (JedisClusterCommandCallback) client -> Converters.toProperties(client.configGet(pattern)), + node) .getValue(); } @@ -502,8 +504,7 @@ class JedisClusterServerCommands implements RedisClusterServerCommands { * @see org.springframework.data.redis.connection.RedisServerCommands#migrate(byte[], org.springframework.data.redis.connection.RedisNode, int, org.springframework.data.redis.connection.RedisServerCommands.MigrateOption, long) */ @Override - public void migrate(byte[] key, RedisNode target, int dbIndex, @Nullable MigrateOption option, - long timeout) { + public void migrate(byte[] key, RedisNode target, int dbIndex, @Nullable MigrateOption option, long timeout) { Assert.notNull(key, "Key must not be null!"); Assert.notNull(target, "Target node must not be null!"); @@ -511,8 +512,8 @@ class JedisClusterServerCommands implements RedisClusterServerCommands { RedisClusterNode node = connection.getTopologyProvider().getTopology().lookup(target.getHost(), target.getPort()); - executeCommandOnSingleNode(client -> client.migrate(target.getHost(), target.getPort(), - key, dbIndex, timeoutToUse), node); + executeCommandOnSingleNode(client -> client.migrate(target.getHost(), target.getPort(), key, dbIndex, timeoutToUse), + node); } private Long convertListOfStringToTime(List serverTimeInformation) { diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStringCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStringCommands.java index ba19d3fe3..fba3f39b2 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStringCommands.java @@ -158,12 +158,11 @@ class JedisClusterStringCommands implements RedisStringCommands { } } else { - SetParams params = JedisConverters.toSetCommandNxXxArgument(option, null); - JedisConverters.toSetCommandExPxArgument(expiration, params); + SetParams params = JedisConverters.toSetCommandNxXxArgument(option); + JedisConverters.toSetCommandExPxArgument(expiration, params); try { - return Converters - .stringToBoolean(connection.getCluster().set(key, value, params)); + return Converters.stringToBoolean(connection.getCluster().set(key, value, params)); } catch (Exception ex) { throw convertJedisAccessException(ex); } @@ -491,7 +490,6 @@ class JedisClusterStringCommands implements RedisStringCommands { } } - /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisStringCommands#bitOp(org.springframework.data.redis.connection.RedisStringCommands.BitOperation, byte[], byte[][]) 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 4645d7650..ad9918753 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 @@ -642,8 +642,7 @@ class JedisClusterZSetCommands implements RedisZSetCommands { if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) { - ZParams zparams = new ZParams().weights(weights.toArray()) - .aggregate(ZParams.Aggregate.valueOf(aggregate.name())); + ZParams zparams = new ZParams().weights(weights.toArray()).aggregate(ZParams.Aggregate.valueOf(aggregate.name())); try { return connection.getCluster().zunionstore(destKey, zparams, sets); @@ -680,7 +679,6 @@ class JedisClusterZSetCommands implements RedisZSetCommands { throw new InvalidDataAccessApiUsageException("ZINTERSTORE can only be executed when all keys map to the same slot"); } - /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisZSetCommands#zInterStore(byte[], org.springframework.data.redis.connection.RedisZSetCommands.Aggregate, org.springframework.data.redis.connection.RedisZSetCommands.Weights, byte[][]) @@ -698,8 +696,7 @@ class JedisClusterZSetCommands implements RedisZSetCommands { if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) { - ZParams zparams = new ZParams().weights(weights.toArray()) - .aggregate(ZParams.Aggregate.valueOf(aggregate.name())); + ZParams zparams = new ZParams().weights(weights.toArray()).aggregate(ZParams.Aggregate.valueOf(aggregate.name())); try { return connection.getCluster().zinterstore(destKey, zparams, sets); diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java index 68949a754..7b97f5e2d 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java @@ -38,7 +38,6 @@ import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.ExceptionTranslationStrategy; import org.springframework.data.redis.FallbackExceptionTranslationStrategy; -import org.springframework.data.redis.RedisConnectionFailureException; import org.springframework.data.redis.RedisSystemException; import org.springframework.data.redis.connection.*; import org.springframework.data.redis.connection.convert.TransactionResultConverter; @@ -62,6 +61,7 @@ import org.springframework.util.StringUtils; * @author Milan Agatonovic * @author Mark Paluch * @author Ninad Divadkar + * @author Guy Korland */ public class JedisConnection extends AbstractRedisConnection { 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 50cbd5970..1966c7465 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 @@ -88,6 +88,7 @@ import org.springframework.util.StringUtils; * @author Jungtaek Lim * @author Mark Paluch * @author Ninad Divadkar + * @author Guy Korland */ abstract public class JedisConverters extends Converters { @@ -196,7 +197,8 @@ abstract public class JedisConverters extends Converters { }; GEO_COORDINATE_TO_POINT_CONVERTER = geoCoordinate -> geoCoordinate != null - ? new Point(geoCoordinate.getLongitude(), geoCoordinate.getLatitude()) : 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() { @@ -220,8 +222,7 @@ abstract public class JedisConverters extends Converters { if (command instanceof BitFieldIncrBy) { - BitFieldIncrBy.Overflow overflow = ((BitFieldIncrBy) command) - .getOverflow(); + BitFieldIncrBy.Overflow overflow = ((BitFieldIncrBy) command).getOverflow(); if (overflow != null) { args.add(JedisConverters.toBytes("OVERFLOW")); args.add(JedisConverters.toBytes(overflow.name())); @@ -498,51 +499,94 @@ abstract public class JedisConverters extends Converters { } /** - * Converts a given {@link Expiration} to the according {@code SET} command argument.
+ * Converts a given {@link Expiration} to the according {@code SET} command argument. *
- *
{@link TimeUnit#SECONDS}
- *
{@code EX}
*
{@link TimeUnit#MILLISECONDS}
*
{@code PX}
+ *
{@link TimeUnit#SECONDS}
+ *
{@code EX}
*
* - * @param expiration + * @param expiration must not be {@literal null}. * @return - * @since 1.7 + * @since 2.2 + */ + public static SetParams toSetCommandExPxArgument(Expiration expiration) { + return toSetCommandExPxArgument(expiration, SetParams.setParams()); + } + + /** + * Converts a given {@link Expiration} to the according {@code SET} command argument. + *
+ *
{@link TimeUnit#MILLISECONDS}
+ *
{@code PX}
+ *
{@link TimeUnit#SECONDS}
+ *
{@code EX}
+ *
+ * + * @param expiration must not be {@literal null}. + * @param params + * @return + * @since 2.2 */ public static SetParams toSetCommandExPxArgument(Expiration expiration, SetParams params) { - params = params==null? SetParams.setParams() : params; - if(expiration.getTimeUnit() == TimeUnit.MILLISECONDS) { - return params.px(expiration.getExpirationTime()); - } - return params.ex((int)expiration.getExpirationTime()); - } + + SetParams paramsToUse = params == null ? SetParams.setParams() : params; + + if (expiration.getTimeUnit() == TimeUnit.MILLISECONDS) { + return paramsToUse.px(expiration.getExpirationTime()); + } + + return paramsToUse.ex((int) expiration.getExpirationTime()); + } /** * Converts a given {@link SetOption} to the according {@code SET} command argument.
*
- *
{@link SetOption#UPSERT}
- *
{@code byte[0]}
- *
{@link SetOption#SET_IF_ABSENT}
- *
{@code NX}
*
{@link SetOption#SET_IF_PRESENT}
*
{@code XX}
+ *
{@link SetOption#SET_IF_ABSENT}
+ *
{@code NX}
+ *
{@link SetOption#UPSERT}
+ *
{@code byte[0]}
*
* - * @param option + * @param option must not be {@literal null}. * @return - * @since 1.7 + * @since 2.2 + */ + public static SetParams toSetCommandNxXxArgument(SetOption option) { + return toSetCommandNxXxArgument(option, SetParams.setParams()); + } + + /** + * Converts a given {@link SetOption} to the according {@code SET} command argument.
+ *
+ *
{@link SetOption#SET_IF_PRESENT}
+ *
{@code XX}
+ *
{@link SetOption#SET_IF_ABSENT}
+ *
{@code NX}
+ *
{@link SetOption#UPSERT}
+ *
{@code byte[0]}
+ *
+ * + * @param option must not be {@literal null}. + * @param params + * @return + * @since 2.2 */ public static SetParams toSetCommandNxXxArgument(SetOption option, SetParams params) { - params = params==null? SetParams.setParams() : params; - switch(option) { - case SET_IF_PRESENT: - return params.xx(); - case SET_IF_ABSENT: - return params.nx(); - default: - return params; - } + + SetParams paramsToUse = params == null ? SetParams.setParams() : params; + + switch (option) { + case SET_IF_PRESENT: + return paramsToUse.xx(); + case SET_IF_ABSENT: + return paramsToUse.nx(); + default: + return paramsToUse; + } } private static byte[] boundaryToBytes(Boundary boundary, byte[] inclPrefix, byte[] exclPrefix) { diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisExceptionConverter.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisExceptionConverter.java index 43b286814..9ecd3f1fa 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisExceptionConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisExceptionConverter.java @@ -15,6 +15,11 @@ */ package org.springframework.data.redis.connection.jedis; +import redis.clients.jedis.exceptions.JedisClusterMaxAttemptsException; +import redis.clients.jedis.exceptions.JedisConnectionException; +import redis.clients.jedis.exceptions.JedisException; +import redis.clients.jedis.exceptions.JedisRedirectionException; + import java.io.IOException; import java.net.UnknownHostException; @@ -25,18 +30,14 @@ import org.springframework.data.redis.ClusterRedirectException; import org.springframework.data.redis.RedisConnectionFailureException; import org.springframework.data.redis.TooManyClusterRedirectionsException; -import redis.clients.jedis.exceptions.JedisClusterMaxAttemptsException; -import redis.clients.jedis.exceptions.JedisConnectionException; -import redis.clients.jedis.exceptions.JedisDataException; -import redis.clients.jedis.exceptions.JedisException; -import redis.clients.jedis.exceptions.JedisRedirectionException; - /** * Converts Exceptions thrown from Jedis to {@link DataAccessException}s * * @author Jennifer Hickey * @author Thomas Darimont * @author Christoph Strobl + * @author Guy Korland + * @author Mark Paluch */ public class JedisExceptionConverter implements Converter { @@ -49,29 +50,29 @@ public class JedisExceptionConverter implements Converter values) { @@ -241,6 +233,22 @@ public class JedisConvertersUnitTests { } } + private static String toString(SetParams setParams) { + + StringBuilder builder = new StringBuilder(); + + for (byte[] parameter : setParams.getByteParams()) { + + if (builder.length() != 0) { + builder.append(' '); + } + + builder.append(new String(parameter)); + } + + return builder.toString(); + } + private Map getRedisServerInfoMap(String name, int port) { Map map = new HashMap<>(); map.put("name", name); diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisItegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisIntegrationTests.java similarity index 83% rename from src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisItegrationTests.java rename to src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisIntegrationTests.java index 16f248e90..e0c66a25e 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisItegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisIntegrationTests.java @@ -19,15 +19,14 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.SettingsUtils; import org.springframework.data.redis.connection.AbstractTransactionalTestBase; -import org.springframework.data.redis.connection.jedis.TransactionalJedisItegrationTests.JedisContextConfiguration; import org.springframework.test.context.ContextConfiguration; /** * @author Christoph Strobl * @author Mark Paluch */ -@ContextConfiguration(classes = { JedisContextConfiguration.class }) -public class TransactionalJedisItegrationTests extends AbstractTransactionalTestBase { +@ContextConfiguration +public class TransactionalJedisIntegrationTests extends AbstractTransactionalTestBase { @Configuration public static class JedisContextConfiguration extends RedisContextConfiguration {