From 2809186e4d28a7dd8c9d0a70c590005a70960372 Mon Sep 17 00:00:00 2001 From: Roman Osadchuk Date: Fri, 10 May 2024 18:00:39 +0300 Subject: [PATCH 001/187] Use correct bitfield `offset` for `INCRBY` using Lettuce. Closes #2903 Original pull request: #2901 --- .../connection/lettuce/LettuceConverters.java | 3 ++- .../AbstractConnectionIntegrationTests.java | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java index d4aa2d9cc8..354217c548 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java @@ -79,6 +79,7 @@ * @author Chris Bono * @author Vikas Garg * @author John Blum + * @author Roman Osadchuk */ @SuppressWarnings("ConstantConditions") public abstract class LettuceConverters extends Converters { @@ -720,7 +721,7 @@ public static BitFieldArgs toBitFieldArgs(BitFieldSubCommands subCommands) { args = args.overflow(type); } - args = args.incrBy(bitFieldType, (int) subCommand.getOffset().getValue(), ((BitFieldIncrBy) subCommand).getValue()); + args = args.incrBy(bitFieldType, offset, ((BitFieldIncrBy) subCommand).getValue()); } } diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index 1d96b83a2f..d6de0e360b 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -45,6 +45,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.domain.Range; @@ -110,6 +112,7 @@ * @author Andrey Shlykov * @author Hendrik Duerkop * @author Shyngys Sapraliyev + * @author Roman Osadchuk */ public abstract class AbstractConnectionIntegrationTests { @@ -3577,6 +3580,23 @@ void bitFieldIncrByWithOverflowShouldWorkCorrectly() { assertThat(results.get(3)).isNotNull(); } + @ParameterizedTest // DATAREDIS-2903 + @ValueSource(booleans = {false, true}) + void bitFieldIncrByAndThenGetShouldWorkCorrectly(boolean isMultipliedByTypeLengthOffset) { + var offset = isMultipliedByTypeLengthOffset + ? BitFieldSubCommands.Offset.offset(300L).multipliedByTypeLength() + : BitFieldSubCommands.Offset.offset(400L); + + actual.add(connection.bitfield(KEY_1, create().incr(INT_8).valueAt(offset).by(1L))); + actual.add(connection.bitfield(KEY_1, create().get(INT_8).valueAt(offset))); + + List results = getResults(); + + assertThat(results).hasSize(2) + // should return same results after INCRBY and GET operations for bitfield with same offset + .containsExactly(List.of(1L), List.of(1L)); + } + @Test // DATAREDIS-562 void bitfieldShouldAllowMultipleSubcommands() { From 7ca2779ea583d00dace9d94946c9c6bce45c58d6 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 13 May 2024 15:09:00 +0200 Subject: [PATCH 002/187] Polishing. Reformat code. --- .../redis/connection/BitFieldSubCommands.java | 12 ++-- .../AbstractConnectionIntegrationTests.java | 69 ++++++++----------- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java b/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java index bb24d05634..4e4193e982 100644 --- a/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java @@ -648,13 +648,13 @@ public static class BitFieldSet extends AbstractBitFieldSubCommand { * @return * @since 2.5.2 */ - public static BitFieldSet create(BitFieldType type,Offset offset,long value){ + public static BitFieldSet create(BitFieldType type, Offset offset, long value) { Assert.notNull(type, "BitFieldType must not be null"); Assert.notNull(offset, "Offset must not be null"); BitFieldSet instance = new BitFieldSet(); - instance.type = type; + instance.type = type; instance.offset = offset; instance.value = value; @@ -728,13 +728,13 @@ public static class BitFieldGet extends AbstractBitFieldSubCommand { * @since 2.5.2 * @return */ - public static BitFieldGet create(BitFieldType type,Offset offset){ + public static BitFieldGet create(BitFieldType type, Offset offset) { Assert.notNull(type, "BitFieldType must not be null"); Assert.notNull(offset, "Offset must not be null"); BitFieldGet instance = new BitFieldGet(); - instance.type = type; + instance.type = type; instance.offset = offset; return instance; @@ -767,7 +767,7 @@ public static class BitFieldIncrBy extends AbstractBitFieldSubCommand { * @return * @since 2.5.2 */ - public static BitFieldIncrBy create(BitFieldType type,Offset offset,long value){ + public static BitFieldIncrBy create(BitFieldType type, Offset offset, long value) { return create(type, offset, value, null); } @@ -787,7 +787,7 @@ public static BitFieldIncrBy create(BitFieldType type, Offset offset, long value Assert.notNull(offset, "Offset must not be null"); BitFieldIncrBy instance = new BitFieldIncrBy(); - instance.type = type; + instance.type = type; instance.offset = offset; instance.value = value; instance.overflow = overflow; diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index d6de0e360b..b6b9cd6ed4 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -132,7 +132,8 @@ public abstract class AbstractConnectionIntegrationTests { protected List actual = new ArrayList<>(); - @Autowired @EnabledOnRedisDriver.DriverQualifier protected RedisConnectionFactory connectionFactory; + @Autowired + @EnabledOnRedisDriver.DriverQualifier protected RedisConnectionFactory connectionFactory; protected RedisConnection byteConnection; @@ -588,8 +589,7 @@ public void testNullKey() { try { connection.decr((String) null); fail("Decrement should fail with null key"); - } catch (Exception expected) { - } + } catch (Exception expected) {} } @Test @@ -601,8 +601,7 @@ public void testNullValue() { try { connection.append(key, null); fail("Append should fail with null value"); - } catch (DataAccessException expected) { - } + } catch (DataAccessException expected) {} } @Test @@ -613,8 +612,7 @@ public void testHashNullKey() { try { connection.hExists(key, null); fail("hExists should fail with null key"); - } catch (DataAccessException expected) { - } + } catch (DataAccessException expected) {} } @Test @@ -627,8 +625,7 @@ public void testHashNullValue() { try { connection.hSet(key, field, null); fail("hSet should fail with null value"); - } catch (DataAccessException expected) { - } + } catch (DataAccessException expected) {} } @Test @@ -665,8 +662,7 @@ public void testPubSubWithNamedChannels() throws Exception { try { Thread.sleep(500); - } catch (InterruptedException ignore) { - } + } catch (InterruptedException ignore) {} // open a new connection RedisConnection connection2 = connectionFactory.getConnection(); @@ -709,8 +705,7 @@ public void testPubSubWithPatterns() throws Exception { try { Thread.sleep(500); - } catch (InterruptedException ignore) { - } + } catch (InterruptedException ignore) {} // open a new connection RedisConnection connection2 = connectionFactory.getConnection(); @@ -2641,8 +2636,7 @@ void scanShouldReadEntireValueRange() { void scanWithType() { assumeThat(isPipelinedOrQueueingConnection(connection)) - .describedAs("SCAN is only available in non-pipeline | non-queueing mode") - .isFalse(); + .describedAs("SCAN is only available in non-pipeline | non-queueing mode").isFalse(); connection.set("key", "data"); connection.lPush("list", "foo"); @@ -2669,7 +2663,8 @@ private static List toList(Cursor cursor) { public void scanShouldReadEntireValueRangeWhenIndividualScanIterationsReturnEmptyCollection() { byteConnection.openPipeline(); - IntStream.range(0, 100).forEach(it -> byteConnection.stringCommands().set("key:%s".formatted(it).getBytes(StandardCharsets.UTF_8), "data".getBytes(StandardCharsets.UTF_8))); + IntStream.range(0, 100).forEach(it -> byteConnection.stringCommands() + .set("key:%s".formatted(it).getBytes(StandardCharsets.UTF_8), "data".getBytes(StandardCharsets.UTF_8))); byteConnection.closePipeline(); Cursor cursor = connection.scan(ScanOptions.scanOptions().match("key*9").count(10).build()); @@ -3364,8 +3359,7 @@ void geoSearchShouldConsiderDistanceCorrectly() { actual.add(connection.geoAdd(key, Arrays.asList(ARIGENTO, CATANIA, PALERMO))); actual.add( - connection.geoSearch(key, GeoReference.fromMember(PALERMO), - GeoShape.byRadius(new Distance(200, KILOMETERS)), + connection.geoSearch(key, GeoReference.fromMember(PALERMO), GeoShape.byRadius(new Distance(200, KILOMETERS)), newGeoSearchArgs().limit(2).includeDistance().includeCoordinates())); List results = getResults(); @@ -3383,8 +3377,7 @@ void geoSearchStoreByMemberShouldStoreResult() { actual.add(connection.geoAdd(key, Arrays.asList(ARIGENTO, CATANIA, PALERMO))); actual.add(connection.geoSearchStore("georesults", key, GeoReference.fromMember(PALERMO), - GeoShape.byRadius(new Distance(200, KILOMETERS)), - newGeoSearchStoreArgs().limit(2).storeDistance())); + GeoShape.byRadius(new Distance(200, KILOMETERS)), newGeoSearchStoreArgs().limit(2).storeDistance())); actual.add(connection.zScore("georesults", PALERMO.getName())); actual.add(connection.zScore("georesults", ARIGENTO.getName())); @@ -3402,8 +3395,7 @@ void geoSearchStoreByPointShouldStoreResult() { actual.add(connection.geoAdd(key, Arrays.asList(ARIGENTO, CATANIA, PALERMO))); actual.add(connection.geoSearchStore("georesults", key, GeoReference.fromCoordinate(PALERMO), - GeoShape.byRadius(new Distance(200, KILOMETERS)), - newGeoSearchStoreArgs().limit(2).storeDistance())); + GeoShape.byRadius(new Distance(200, KILOMETERS)), newGeoSearchStoreArgs().limit(2).storeDistance())); actual.add(connection.zScore("georesults", PALERMO.getName())); actual.add(connection.zScore("georesults", ARIGENTO.getName())); @@ -3580,22 +3572,21 @@ void bitFieldIncrByWithOverflowShouldWorkCorrectly() { assertThat(results.get(3)).isNotNull(); } - @ParameterizedTest // DATAREDIS-2903 - @ValueSource(booleans = {false, true}) - void bitFieldIncrByAndThenGetShouldWorkCorrectly(boolean isMultipliedByTypeLengthOffset) { - var offset = isMultipliedByTypeLengthOffset - ? BitFieldSubCommands.Offset.offset(300L).multipliedByTypeLength() - : BitFieldSubCommands.Offset.offset(400L); + @ParameterizedTest // GH-2903 + @ValueSource(booleans = { false, true }) + void bitFieldIncrByAndThenGetShouldWorkCorrectly(boolean isMultipliedByTypeLengthOffset) { - actual.add(connection.bitfield(KEY_1, create().incr(INT_8).valueAt(offset).by(1L))); - actual.add(connection.bitfield(KEY_1, create().get(INT_8).valueAt(offset))); + var offset = isMultipliedByTypeLengthOffset ? BitFieldSubCommands.Offset.offset(300L).multipliedByTypeLength() + : BitFieldSubCommands.Offset.offset(400L); - List results = getResults(); + actual.add(connection.bitfield(KEY_1, create().incr(INT_8).valueAt(offset).by(1L))); + actual.add(connection.bitfield(KEY_1, create().get(INT_8).valueAt(offset))); - assertThat(results).hasSize(2) - // should return same results after INCRBY and GET operations for bitfield with same offset - .containsExactly(List.of(1L), List.of(1L)); - } + List results = getResults(); + + // should return same results after INCRBY and GET operations for bitfield with same offset + assertThat(results).containsExactly(List.of(1L), List.of(1L)); + } @Test // DATAREDIS-562 void bitfieldShouldAllowMultipleSubcommands() { @@ -3884,8 +3875,8 @@ public void xPendingShouldLoadPendingMessagesForConsumer() { actual.add(connection.xReadGroupAsString(Consumer.from("my-group", "my-consumer"), StreamOffset.create(KEY_1, ReadOffset.lastConsumed()))); - actual.add(connection.xPending(KEY_1, "my-group", "my-consumer", - org.springframework.data.domain.Range.unbounded(), 10L)); + actual.add( + connection.xPending(KEY_1, "my-group", "my-consumer", org.springframework.data.domain.Range.unbounded(), 10L)); List results = getResults(); assertThat(results).hasSize(4); @@ -4106,7 +4097,7 @@ public void xinfoConsumersNoConsumer() { assertThat(info.size()).isZero(); } - @Test //GH-2345 + @Test // GH-2345 public void zRangeStoreByScoreStoresKeys() { String dstKey = KEY_2; String srcKey = KEY_1; @@ -4144,7 +4135,7 @@ public void zRangeStoreRevByScoreStoresKeys() { assertThat((LinkedHashSet) result.get(5)).containsSequence(VALUE_3, VALUE_4); } - @Test //GH-2345 + @Test // GH-2345 public void zRangeStoreByLexStoresKeys() { String dstKey = KEY_2; String srcKey = KEY_1; From 933d5e393e7b79c79a695a5df293a01e33460b59 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 May 2024 11:49:11 +0200 Subject: [PATCH 003/187] Prepare 3.3 GA (2024.0.0). See #2893 --- pom.xml | 22 +++++----------------- src/main/resources/notice.txt | 3 ++- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 507b76dd15..03ca1b84d3 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.3.0-SNAPSHOT + 3.3.0 - 3.3.0-SNAPSHOT - 3.3.0-SNAPSHOT + 3.3.0 + 3.3.0 4.0.2 1.9.4 1.4.20 @@ -387,19 +387,7 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - - - spring-milestone - https://repo.spring.io/milestone - + + diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 5b4c9ef0b7..b33d38469b 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 3.3 RC1 (2024.0.0) +Spring Data Redis 3.3 GA (2024.0.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -52,5 +52,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From 94c7f26aecd8bf4893a8f9a7013f3522d705e6f8 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 May 2024 11:49:25 +0200 Subject: [PATCH 004/187] Release version 3.3 GA (2024.0.0). See #2893 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 03ca1b84d3..44cf4b718f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.3.0-SNAPSHOT + 3.3.0 Spring Data Redis Spring Data module for Redis From 3d6a410ee9a747b0b475102458a5ea08d8d9ae78 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 May 2024 11:51:33 +0200 Subject: [PATCH 005/187] Prepare next development iteration. See #2893 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44cf4b718f..b7bcf0e1cb 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.3.0 + 3.4.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From ea84629bacab01193b7da137d344341e86753739 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 May 2024 11:51:34 +0200 Subject: [PATCH 006/187] After release cleanups. See #2893 --- pom.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index b7bcf0e1cb..e19502b4eb 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.3.0 + 3.4.0-SNAPSHOT - 3.3.0 - 3.3.0 + 3.4.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.2 1.9.4 1.4.20 @@ -387,7 +387,19 @@ - - + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + + + spring-milestone + https://repo.spring.io/milestone + From 33326aa2db5c1ca20b6a59fca83e8ffc855c2c05 Mon Sep 17 00:00:00 2001 From: annemayor Date: Sat, 11 May 2024 22:48:55 +0900 Subject: [PATCH 007/187] Allow configuring custom `NullValueSerializer`. Closes #2878 Original pull request: #2905 --- .../GenericJackson2JsonRedisSerializer.java | 74 +++++++++++++++++++ ...cJackson2JsonRedisSerializerUnitTests.java | 53 ++++++++++++- 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index 28b9474b26..6befb31e3b 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -134,6 +134,17 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName this.mapper.setDefaultTyping(typer); } + /** + * Factory method returning a {@literal Builder} used to construct and configure a {@link GenericJackson2JsonRedisSerializer}. + * + * @return new {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. + * @since 3.3 + */ + public static GenericJackson2JsonRedisSerializerBuilder builder(ObjectMapper objectMapper, JacksonObjectReader reader, + JacksonObjectWriter writer) { + return new GenericJackson2JsonRedisSerializerBuilder(objectMapper, reader, writer); + } + /** * Setting a custom-configured {@link ObjectMapper} is one way to take further control of the JSON serialization * process. For example, an extended {@link SerializerFactory} can be configured that provides custom serializers for @@ -396,6 +407,69 @@ public void serializeWithType(NullValue value, JsonGenerator jsonGenerator, Seri } } + /** + * {@literal Builder} for creating a {@link GenericJackson2JsonRedisSerializer}. + * + * @author Anne Lee + * @since 3.3 + */ + public static class GenericJackson2JsonRedisSerializerBuilder { + @Nullable + private String classPropertyTypeName; + private JacksonObjectReader reader; + private JacksonObjectWriter writer; + private ObjectMapper mapper; + @Nullable + private StdSerializer nullValueSerializer; + + private GenericJackson2JsonRedisSerializerBuilder( + ObjectMapper objectMapper, + JacksonObjectReader reader, + JacksonObjectWriter writer + ) { + this.mapper = objectMapper; + this.reader = reader; + this.writer = writer; + } + + /** + * Configure a classPropertyName. + * + * @param classPropertyTypeName can be {@literal null}. + * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. + * @since 3.3 + */ + public GenericJackson2JsonRedisSerializerBuilder classPropertyTypeName(@Nullable String classPropertyTypeName) { + this.classPropertyTypeName = classPropertyTypeName; + return this; + } + + /** + * Register a nullValueSerializer. + * + * @param nullValueSerializer the {@link StdSerializer} to use for {@link NullValue} serialization. Can be {@literal null}. + * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. + */ + public GenericJackson2JsonRedisSerializerBuilder registerNullValueSerializer(@Nullable StdSerializer nullValueSerializer) { + this.nullValueSerializer = nullValueSerializer; + return this; + } + + /** + * Create new instance of {@link GenericJackson2JsonRedisSerializer} with configuration options applied. + * + * @return new instance of {@link GenericJackson2JsonRedisSerializer}. + */ + public GenericJackson2JsonRedisSerializer build() { + Assert.notNull(this.mapper, "ObjectMapper must not be null"); + Assert.notNull(this.reader, "Reader must not be null"); + Assert.notNull(this.writer, "Writer must not be null"); + + this.mapper.registerModule(new SimpleModule().addSerializer(this.nullValueSerializer != null ? this.nullValueSerializer : new NullValueSerializer(this.classPropertyTypeName))); + return new GenericJackson2JsonRedisSerializer(this.mapper, this.reader, this.writer, this.classPropertyTypeName); + } + } + /** * Custom {@link StdTypeResolverBuilder} that considers typing for non-primitive types. Primitives, their wrappers and * primitive arrays do not require type hints. The default {@code DefaultTyping#EVERYTHING} typing does not satisfy diff --git a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java index 7f54a324cf..effbeae471 100644 --- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java @@ -40,6 +40,10 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -420,7 +424,7 @@ void deserializesJavaTimeFrimBytes() { } @Test // GH-2601 - public void internalObjectMapperCustomization() { + void internalObjectMapperCustomization() { GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(); @@ -432,13 +436,13 @@ public void internalObjectMapperCustomization() { assertThat(serializer.configure(configurer)).isSameAs(serializer); - verify(mockObjectMapper, times(1)).registerModule(eq(mockModule)); + verify(mockObjectMapper, times(1)).registerModule(mockModule); verifyNoMoreInteractions(mockObjectMapper); verifyNoInteractions(mockModule); } @Test // GH-2601 - public void configureWithNullConsumerThrowsIllegalArgumentException() { + void configureWithNullConsumerThrowsIllegalArgumentException() { assertThatIllegalArgumentException() .isThrownBy(() -> new GenericJackson2JsonRedisSerializer().configure(null)) @@ -446,6 +450,49 @@ public void configureWithNullConsumerThrowsIllegalArgumentException() { .withNoCause(); } + @Test + void defaultSerializeAndDeserializeNullValueWithBuilderClass() { + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder( + new ObjectMapper().enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY), + JacksonObjectReader.create(), JacksonObjectWriter.create()) + .classPropertyTypeName(null) + .registerNullValueSerializer(null) + .build(); + + serializeAndDeserializeNullValue(serializer); + } + + @Test + void customSerializeAndDeserializeNullValueWithBuilderClass() { + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder( + new ObjectMapper(), JacksonObjectReader.create(), JacksonObjectWriter.create()) + .classPropertyTypeName(null) + .registerNullValueSerializer(new StdSerializer<>(NullValue.class) { + @Override + public void serialize(NullValue nullValue, + JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeNull(); + } + + @Override + public void serializeWithType(NullValue value, JsonGenerator jsonGenerator, SerializerProvider serializers, + TypeSerializer typeSerializer) throws IOException { + + serialize(value, jsonGenerator, serializers); + } + }) + .build(); + + NullValue nv = BeanUtils.instantiateClass(NullValue.class); + + byte[] serializedValue = serializer.serialize(nv); + assertThat(serializedValue).isNotNull(); + + Object deserializedValue = serializer.deserialize(serializedValue); + assertThat(deserializedValue).isNull(); + } + private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) { NullValue nv = BeanUtils.instantiateClass(NullValue.class); From 8357c7d88b4e7ee6d8bb73f1a38e602c11ff758c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 21 May 2024 14:55:19 +0200 Subject: [PATCH 008/187] Polishing. Revise builder. Accept builder components in builder methods instead of the builder factory method. Enforce valid parameters instead of lenient, potentially null parameters. Introduce configuration means to control default typing. Extend tests. See #2878 Original pull request: #2905 --- .../GenericJackson2JsonRedisSerializer.java | 237 ++++++++++++------ ...cJackson2JsonRedisSerializerUnitTests.java | 101 ++++---- 2 files changed, 213 insertions(+), 125 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index 6befb31e3b..ae968d6141 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -60,6 +60,7 @@ * @author Mark Paluch * @author Mao Shuai * @author John Blum + * @author Anne Lee * @see org.springframework.data.redis.serializer.JacksonObjectReader * @see org.springframework.data.redis.serializer.JacksonObjectWriter * @see com.fasterxml.jackson.databind.ObjectMapper @@ -92,13 +93,13 @@ public GenericJackson2JsonRedisSerializer() { * In case {@link String name} is {@literal empty} or {@literal null}, then {@link JsonTypeInfo.Id#CLASS} will be * used. * - * @param classPropertyTypeName {@link String name} of the JSON property holding type information; can be + * @param typeHintPropertyName {@link String name} of the JSON property holding type information; can be * {@literal null}. * @see ObjectMapper#activateDefaultTypingAsProperty(PolymorphicTypeValidator, DefaultTyping, String) * @see ObjectMapper#activateDefaultTyping(PolymorphicTypeValidator, DefaultTyping, As) */ - public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName) { - this(classPropertyTypeName, JacksonObjectReader.create(), JacksonObjectWriter.create()); + public GenericJackson2JsonRedisSerializer(@Nullable String typeHintPropertyName) { + this(typeHintPropertyName, JacksonObjectReader.create(), JacksonObjectWriter.create()); } /** @@ -109,7 +110,7 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName * In case {@link String name} is {@literal empty} or {@literal null}, then {@link JsonTypeInfo.Id#CLASS} will be * used. * - * @param classPropertyTypeName {@link String name} of the JSON property holding type information; can be + * @param typeHintPropertyName {@link String name} of the JSON property holding type information; can be * {@literal null}. * @param reader {@link JacksonObjectReader} function to read objects using {@link ObjectMapper}. * @param writer {@link JacksonObjectWriter} function to write objects using {@link ObjectMapper}. @@ -117,32 +118,14 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName * @see ObjectMapper#activateDefaultTyping(PolymorphicTypeValidator, DefaultTyping, As) * @since 3.0 */ - public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName, JacksonObjectReader reader, + public GenericJackson2JsonRedisSerializer(@Nullable String typeHintPropertyName, JacksonObjectReader reader, JacksonObjectWriter writer) { - this(new ObjectMapper(), reader, writer, classPropertyTypeName); + this(new ObjectMapper(), reader, writer, typeHintPropertyName); - registerNullValueSerializer(this.mapper, classPropertyTypeName); + registerNullValueSerializer(this.mapper, typeHintPropertyName); - StdTypeResolverBuilder typer = TypeResolverBuilder.forEverything(this.mapper).init(JsonTypeInfo.Id.CLASS, null) - .inclusion(JsonTypeInfo.As.PROPERTY); - - if (StringUtils.hasText(classPropertyTypeName)) { - typer = typer.typeProperty(classPropertyTypeName); - } - - this.mapper.setDefaultTyping(typer); - } - - /** - * Factory method returning a {@literal Builder} used to construct and configure a {@link GenericJackson2JsonRedisSerializer}. - * - * @return new {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. - * @since 3.3 - */ - public static GenericJackson2JsonRedisSerializerBuilder builder(ObjectMapper objectMapper, JacksonObjectReader reader, - JacksonObjectWriter writer) { - return new GenericJackson2JsonRedisSerializerBuilder(objectMapper, reader, writer); + this.mapper.setDefaultTyping(createDefaultTypeResolverBuilder(getObjectMapper(), typeHintPropertyName)); } /** @@ -188,7 +171,7 @@ private GenericJackson2JsonRedisSerializer(ObjectMapper mapper, JacksonObjectRea this.typeResolver = newTypeResolver(mapper, typeHintPropertyName, this.defaultTypingEnabled); } - private TypeResolver newTypeResolver(ObjectMapper mapper, @Nullable String typeHintPropertyName, + private static TypeResolver newTypeResolver(ObjectMapper mapper, @Nullable String typeHintPropertyName, Lazy defaultTypingEnabled) { Lazy lazyTypeFactory = Lazy.of(mapper::getTypeFactory); @@ -199,19 +182,17 @@ private TypeResolver newTypeResolver(ObjectMapper mapper, @Nullable String typeH return new TypeResolver(lazyTypeFactory, lazyTypeHintPropertyName); } - private Lazy newLazyTypeHintPropertyName(ObjectMapper mapper, Lazy defaultTypingEnabled) { + private static Lazy newLazyTypeHintPropertyName(ObjectMapper mapper, Lazy defaultTypingEnabled) { Lazy configuredTypeDeserializationPropertyName = getConfiguredTypeDeserializationPropertyName(mapper); - Lazy resolvedLazyTypeHintPropertyName = Lazy.of(() -> defaultTypingEnabled.get() ? null - : configuredTypeDeserializationPropertyName.get()); - - resolvedLazyTypeHintPropertyName = resolvedLazyTypeHintPropertyName.or("@class"); + Lazy resolvedLazyTypeHintPropertyName = Lazy + .of(() -> defaultTypingEnabled.get() ? null : configuredTypeDeserializationPropertyName.get()); - return resolvedLazyTypeHintPropertyName; + return resolvedLazyTypeHintPropertyName.or("@class"); } - private Lazy getConfiguredTypeDeserializationPropertyName(ObjectMapper mapper) { + private static Lazy getConfiguredTypeDeserializationPropertyName(ObjectMapper mapper) { return Lazy.of(() -> { @@ -226,20 +207,43 @@ private Lazy getConfiguredTypeDeserializationPropertyName(ObjectMapper m }); } + private static StdTypeResolverBuilder createDefaultTypeResolverBuilder(ObjectMapper objectMapper, + @Nullable String typeHintPropertyName) { + + StdTypeResolverBuilder typer = TypeResolverBuilder.forEverything(objectMapper).init(JsonTypeInfo.Id.CLASS, null) + .inclusion(As.PROPERTY); + + if (StringUtils.hasText(typeHintPropertyName)) { + typer = typer.typeProperty(typeHintPropertyName); + } + return typer; + } + + /** + * Factory method returning a {@literal Builder} used to construct and configure a + * {@link GenericJackson2JsonRedisSerializer}. + * + * @return new {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. + * @since 3.3.1 + */ + public static GenericJackson2JsonRedisSerializerBuilder builder() { + return new GenericJackson2JsonRedisSerializerBuilder(); + } + /** * Register {@link NullValueSerializer} in the given {@link ObjectMapper} with an optional - * {@code classPropertyTypeName}. This method should be called by code that customizes + * {@code typeHintPropertyName}. This method should be called by code that customizes * {@link GenericJackson2JsonRedisSerializer} by providing an external {@link ObjectMapper}. * * @param objectMapper the object mapper to customize. - * @param classPropertyTypeName name of the type property. Defaults to {@code @class} if {@literal null}/empty. + * @param typeHintPropertyName name of the type property. Defaults to {@code @class} if {@literal null}/empty. * @since 2.2 */ - public static void registerNullValueSerializer(ObjectMapper objectMapper, @Nullable String classPropertyTypeName) { + public static void registerNullValueSerializer(ObjectMapper objectMapper, @Nullable String typeHintPropertyName) { // Simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here // since we need the type hint embedded for deserialization using the default typing feature. - objectMapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(classPropertyTypeName))); + objectMapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(typeHintPropertyName))); } /** @@ -376,8 +380,7 @@ protected JavaType resolveType(byte[] source, Class type) throws IOException */ private static class NullValueSerializer extends StdSerializer { - @Serial - private static final long serialVersionUID = 1999052150548658808L; + @Serial private static final long serialVersionUID = 1999052150548658808L; private final String classIdentifier; @@ -408,65 +411,155 @@ public void serializeWithType(NullValue value, JsonGenerator jsonGenerator, Seri } /** - * {@literal Builder} for creating a {@link GenericJackson2JsonRedisSerializer}. + * Builder for configuring and creating a {@link GenericJackson2JsonRedisSerializer}. * * @author Anne Lee - * @since 3.3 + * @author Mark Paluch + * @since 3.3.1 */ public static class GenericJackson2JsonRedisSerializerBuilder { - @Nullable - private String classPropertyTypeName; - private JacksonObjectReader reader; - private JacksonObjectWriter writer; - private ObjectMapper mapper; - @Nullable - private StdSerializer nullValueSerializer; - - private GenericJackson2JsonRedisSerializerBuilder( - ObjectMapper objectMapper, - JacksonObjectReader reader, - JacksonObjectWriter writer - ) { - this.mapper = objectMapper; + + private @Nullable String typeHintPropertyName; + + private JacksonObjectReader reader = JacksonObjectReader.create(); + + private JacksonObjectWriter writer = JacksonObjectWriter.create(); + + private @Nullable ObjectMapper objectMapper; + + private @Nullable Boolean defaultTyping; + + private boolean registerNullValueSerializer = true; + + private @Nullable StdSerializer nullValueSerializer; + + private GenericJackson2JsonRedisSerializerBuilder() {} + + /** + * Enable or disable default typing. Enabling default typing will override + * {@link ObjectMapper#setDefaultTyping(com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder)} for a given + * {@link ObjectMapper}. Default typing is enabled by default if no {@link ObjectMapper} is provided. + * + * @param defaultTyping whether to enable/disable default typing. Enabled by default if the {@link ObjectMapper} is + * not provided. + * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. + */ + public GenericJackson2JsonRedisSerializerBuilder defaultTyping(boolean defaultTyping) { + this.defaultTyping = defaultTyping; + return this; + } + + /** + * Configure a property name to that represents the type hint. + * + * @param typeHintPropertyName {@link String name} of the JSON property holding type information. + * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. + */ + public GenericJackson2JsonRedisSerializerBuilder typeHintPropertyName(String typeHintPropertyName) { + + Assert.hasText(typeHintPropertyName, "Type hint property name must bot be null or empty"); + + this.typeHintPropertyName = typeHintPropertyName; + return this; + } + + /** + * Configure a provided {@link ObjectMapper}. Note that the provided {@link ObjectMapper} can be reconfigured with a + * {@link #nullValueSerializer} or default typing depending on the builder configuration. + * + * @param objectMapper must not be {@literal null}. + * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. + */ + public GenericJackson2JsonRedisSerializerBuilder objectMapper(ObjectMapper objectMapper) { + + Assert.notNull(objectMapper, "ObjectMapper must not be null"); + + this.objectMapper = objectMapper; + return this; + } + + /** + * Configure {@link JacksonObjectReader}. + * + * @param reader must not be {@literal null}. + * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. + */ + public GenericJackson2JsonRedisSerializerBuilder reader(JacksonObjectReader reader) { + + Assert.notNull(reader, "JacksonObjectReader must not be null"); + this.reader = reader; - this.writer = writer; + return this; } /** - * Configure a classPropertyName. + * Configure {@link JacksonObjectWriter}. * - * @param classPropertyTypeName can be {@literal null}. + * @param writer must not be {@literal null}. * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. - * @since 3.3 */ - public GenericJackson2JsonRedisSerializerBuilder classPropertyTypeName(@Nullable String classPropertyTypeName) { - this.classPropertyTypeName = classPropertyTypeName; + public GenericJackson2JsonRedisSerializerBuilder writer(JacksonObjectWriter writer) { + + Assert.notNull(writer, "JacksonObjectWriter must not be null"); + + this.writer = writer; return this; } /** - * Register a nullValueSerializer. + * Register a {@link StdSerializer serializer} for {@link NullValue}. * - * @param nullValueSerializer the {@link StdSerializer} to use for {@link NullValue} serialization. Can be {@literal null}. + * @param nullValueSerializer the {@link StdSerializer} to use for {@link NullValue} serialization, must not be + * {@literal null}. * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. */ - public GenericJackson2JsonRedisSerializerBuilder registerNullValueSerializer(@Nullable StdSerializer nullValueSerializer) { + public GenericJackson2JsonRedisSerializerBuilder nullValueSerializer(StdSerializer nullValueSerializer) { + + Assert.notNull(nullValueSerializer, "Null value serializer must not be null"); + this.nullValueSerializer = nullValueSerializer; return this; } /** - * Create new instance of {@link GenericJackson2JsonRedisSerializer} with configuration options applied. + * Configure whether to register a {@link StdSerializer serializer} for {@link NullValue} serialization. The default + * serializer considers {@link #typeHintPropertyName(String)}. * - * @return new instance of {@link GenericJackson2JsonRedisSerializer}. + * @param registerNullValueSerializer {@code true} to register the default serializer; {@code false} otherwise. + * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}. + */ + public GenericJackson2JsonRedisSerializerBuilder registerNullValueSerializer(boolean registerNullValueSerializer) { + this.registerNullValueSerializer = registerNullValueSerializer; + return this; + } + + /** + * Creates a new instance of {@link GenericJackson2JsonRedisSerializer} with configuration options applied. Creates + * also a new {@link ObjectMapper} if none was provided. + * + * @return a new instance of {@link GenericJackson2JsonRedisSerializer}. */ public GenericJackson2JsonRedisSerializer build() { - Assert.notNull(this.mapper, "ObjectMapper must not be null"); - Assert.notNull(this.reader, "Reader must not be null"); - Assert.notNull(this.writer, "Writer must not be null"); - this.mapper.registerModule(new SimpleModule().addSerializer(this.nullValueSerializer != null ? this.nullValueSerializer : new NullValueSerializer(this.classPropertyTypeName))); - return new GenericJackson2JsonRedisSerializer(this.mapper, this.reader, this.writer, this.classPropertyTypeName); + ObjectMapper objectMapper = this.objectMapper; + boolean providedObjectMapper = objectMapper != null; + + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + } + + if (registerNullValueSerializer) { + objectMapper.registerModule(new SimpleModule("GenericJackson2JsonRedisSerializerBuilder") + .addSerializer(this.nullValueSerializer != null ? this.nullValueSerializer + : new NullValueSerializer(this.typeHintPropertyName))); + } + + if ((!providedObjectMapper && (defaultTyping == null || defaultTyping)) + || (defaultTyping != null && defaultTyping)) { + objectMapper.setDefaultTyping(createDefaultTypeResolverBuilder(objectMapper, typeHintPropertyName)); + } + + return new GenericJackson2JsonRedisSerializer(objectMapper, this.reader, this.writer, this.typeHintPropertyName); } } diff --git a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java index effbeae471..2a6011d952 100644 --- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java @@ -15,20 +15,10 @@ */ package org.springframework.data.redis.serializer; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; -import static org.springframework.test.util.ReflectionTestUtils.getField; -import static org.springframework.util.ObjectUtils.nullSafeEquals; -import static org.springframework.util.ObjectUtils.nullSafeHashCode; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.util.ReflectionTestUtils.*; +import static org.springframework.util.ObjectUtils.*; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -40,10 +30,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -55,13 +41,17 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; @@ -78,9 +68,14 @@ class GenericJackson2JsonRedisSerializerUnitTests { private static final SimpleObject SIMPLE_OBJECT = new SimpleObject(1L); private static final ComplexObject COMPLEX_OBJECT = new ComplexObject("steelheart", SIMPLE_OBJECT); - @Test // DATAREDIS-392 + @Test // DATAREDIS-392, GH-2878 void shouldUseDefaultTyping() { assertThat(extractTypeResolver(new GenericJackson2JsonRedisSerializer())).isNotNull(); + assertThat(extractTypeResolver(GenericJackson2JsonRedisSerializer.builder().build())).isNotNull(); + assertThat(extractTypeResolver(GenericJackson2JsonRedisSerializer.builder().defaultTyping(false).build())).isNull(); + assertThat( + extractTypeResolver(GenericJackson2JsonRedisSerializer.builder().objectMapper(new ObjectMapper()).build())) + .isNull(); } @Test // DATAREDIS-392 @@ -102,12 +97,13 @@ void shouldUseDefaultTypingWhenClassPropertyNameIsNull() { @Test // DATAREDIS-392 void shouldUseDefaultTypingWhenClassPropertyNameIsProvided() { - TypeResolverBuilder typeResolver = extractTypeResolver(new GenericJackson2JsonRedisSerializer("firefight")); + TypeResolverBuilder typeResolver = extractTypeResolver( + GenericJackson2JsonRedisSerializer.builder().typeHintPropertyName("firefight").build()); assertThat((String) getField(typeResolver, "_typeProperty")).isEqualTo("firefight"); } @Test // DATAREDIS-392 - void serializeShouldReturnEmptyByteArrayWhenSouceIsNull() { + void serializeShouldReturnEmptyByteArrayWhenSourceIsNull() { assertThat(new GenericJackson2JsonRedisSerializer().serialize(null)).isEqualTo(SerializationUtils.EMPTY_ARRAY); } @@ -231,10 +227,10 @@ void shouldConsiderWriter() { user.id = 42; user.name = "Walter White"; - GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer((String) null, - JacksonObjectReader.create(), (mapper, source) -> { + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() + .writer((mapper, source) -> { return mapper.writerWithView(Views.Basic.class).writeValueAsBytes(source); - }); + }).build(); byte[] result = serializer.serialize(user); @@ -244,10 +240,10 @@ void shouldConsiderWriter() { @Test // GH-2322 void considersWriterForCustomObjectMapper() { - GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(new ObjectMapper(), - JacksonObjectReader.create(), (mapper, source) -> { + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() + .objectMapper(new ObjectMapper()).writer((mapper, source) -> { return mapper.writerWithView(Views.Basic.class).writeValueAsBytes(source); - }); + }).build(); User user = new User(); user.email = "walter@heisenberg.com"; @@ -267,13 +263,14 @@ void shouldConsiderReader() { user.id = 42; user.name = "Walter White"; - GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer((String) null, + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() + .reader( (mapper, source, type) -> { if (type.getRawClass() == User.class) { return mapper.readerWithView(Views.Basic.class).forType(type).readValue(source); } return mapper.readValue(source, type); - }, JacksonObjectWriter.create()); + }).build(); byte[] serializedValue = serializer.serialize(user); @@ -436,7 +433,7 @@ void internalObjectMapperCustomization() { assertThat(serializer.configure(configurer)).isSameAs(serializer); - verify(mockObjectMapper, times(1)).registerModule(mockModule); + verify(mockObjectMapper).registerModule(mockModule); verifyNoMoreInteractions(mockObjectMapper); verifyNoInteractions(mockModule); } @@ -452,36 +449,33 @@ void configureWithNullConsumerThrowsIllegalArgumentException() { @Test void defaultSerializeAndDeserializeNullValueWithBuilderClass() { - GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder( - new ObjectMapper().enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY), - JacksonObjectReader.create(), JacksonObjectWriter.create()) - .classPropertyTypeName(null) - .registerNullValueSerializer(null) + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() + .objectMapper(new ObjectMapper().enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY)) .build(); serializeAndDeserializeNullValue(serializer); } - @Test - void customSerializeAndDeserializeNullValueWithBuilderClass() { - GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder( - new ObjectMapper(), JacksonObjectReader.create(), JacksonObjectWriter.create()) - .classPropertyTypeName(null) - .registerNullValueSerializer(new StdSerializer<>(NullValue.class) { - @Override - public void serialize(NullValue nullValue, - JsonGenerator jsonGenerator, - SerializerProvider serializerProvider) throws IOException { - jsonGenerator.writeNull(); - } + @Test // GH-2878 + void customNullValueSerializer() { - @Override - public void serializeWithType(NullValue value, JsonGenerator jsonGenerator, SerializerProvider serializers, - TypeSerializer typeSerializer) throws IOException { + StdSerializer nullValueSerializer = new StdSerializer<>(NullValue.class) { + @Override + public void serialize(NullValue nullValue, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException { + jsonGenerator.writeNull(); + } - serialize(value, jsonGenerator, serializers); - } - }) + @Override + public void serializeWithType(NullValue value, JsonGenerator jsonGenerator, SerializerProvider serializers, + TypeSerializer typeSerializer) throws IOException { + + serialize(value, jsonGenerator, serializers); + } + }; + + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() + .nullValueSerializer(nullValueSerializer) .build(); NullValue nv = BeanUtils.instantiateClass(NullValue.class); @@ -504,6 +498,7 @@ private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSer assertThat(deserializedValue).isInstanceOf(NullValue.class); } + @Nullable private TypeResolverBuilder extractTypeResolver(GenericJackson2JsonRedisSerializer serializer) { ObjectMapper mapper = (ObjectMapper) getField(serializer, "mapper"); From f11c9f1cd509f2707e9353121bea5db6c4f1901d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 21 May 2024 14:57:28 +0200 Subject: [PATCH 009/187] Polishing. Remove lingering asciidoc Maven plugin. See #2893 --- pom.xml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pom.xml b/pom.xml index e19502b4eb..d129d27346 100644 --- a/pom.xml +++ b/pom.xml @@ -317,17 +317,6 @@ maven-assembly-plugin - - org.asciidoctor - asciidoctor-maven-plugin - - - ${lettuce} - ${jedis} - - - - From d996de2b0798284b92e8bce5efe768592dc249c9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 31 May 2024 09:26:55 +0200 Subject: [PATCH 010/187] Provide class loader to ProxyFactory to create BoundOperation proxies. Closes #2920 --- .../data/redis/core/BoundOperationsProxyFactory.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java b/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java index a65b1e200c..423cd3b4fd 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java +++ b/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java @@ -70,7 +70,7 @@ public T createProxy(Class boundOperationsInterface, Object key, DataType proxyFactory.addAdvice( new BoundOperationsMethodInterceptor(key, operations, boundOperationsInterface, operationsTarget, delegate)); - return (T) proxyFactory.getProxy(); + return (T) proxyFactory.getProxy(getClass().getClassLoader()); } Method lookupRequiredMethod(Method method, Class targetClass, boolean considerKeyArgument) { @@ -144,9 +144,9 @@ public Object invoke(MethodInvocation invocation) throws Throwable { yield null; } case "getOperations" -> delegate.getOps(); - default -> method.getDeclaringClass() == boundOperationsInterface - ? doInvoke(invocation, method, operationsTarget, true) - : doInvoke(invocation, method, delegate, false); + default -> + method.getDeclaringClass() == boundOperationsInterface ? doInvoke(invocation, method, operationsTarget, true) + : doInvoke(invocation, method, delegate, false); }; } From 1e6c1e8c0ed0e0757ff746adc761185c4ea698e0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 4 Jun 2024 08:28:56 +0200 Subject: [PATCH 011/187] Introduce placeholders to `Makefile` to configure server backends. Closes #2922 --- Makefile | 86 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index 35334f506d..7d58ad6575 100644 --- a/Makefile +++ b/Makefile @@ -12,16 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -REDIS_VERSION:=7.2.4 +REDIS_VERSION:=7.2.5 +PROJECT?=redis +GH_ORG?=redis SPRING_PROFILE?=ci SHELL=/bin/bash -euo pipefail ####### # Redis ####### -.PRECIOUS: work/redis-%.conf +.PRECIOUS: work/$(PROJECT)-%.conf -work/redis-%.conf: +work/$(PROJECT)-%.conf: @mkdir -p $(@D) echo port $* >> $@ @@ -29,15 +31,15 @@ work/redis-%.conf: echo protected-mode no >> $@ echo bind 0.0.0.0 >> $@ echo notify-keyspace-events Ex >> $@ - echo pidfile $(shell pwd)/work/redis-$*.pid >> $@ - echo logfile $(shell pwd)/work/redis-$*.log >> $@ - echo unixsocket $(shell pwd)/work/redis-$*.sock >> $@ + echo pidfile $(shell pwd)/work/$(PROJECT)-$*.pid >> $@ + echo logfile $(shell pwd)/work/$(PROJECT)-$*.log >> $@ + echo unixsocket $(shell pwd)/work/$(PROJECT)-$*.sock >> $@ echo unixsocketperm 755 >> $@ echo save \"\" >> $@ echo slaveof 127.0.0.1 6379 >> $@ # Handled separately because it's a node with authentication. User: spring, password: data. Default password: foobared -work/redis-6382.conf: +work/$(PROJECT)-6382.conf: @mkdir -p $(@D) echo port 6382 >> $@ @@ -45,9 +47,9 @@ work/redis-6382.conf: echo protected-mode no >> $@ echo bind 0.0.0.0 >> $@ echo notify-keyspace-events Ex >> $@ - echo pidfile $(shell pwd)/work/redis-6382.pid >> $@ - echo logfile $(shell pwd)/work/redis-6382.log >> $@ - echo unixsocket $(shell pwd)/work/redis-6382.sock >> $@ + echo pidfile $(shell pwd)/work/$(PROJECT)-6382.pid >> $@ + echo logfile $(shell pwd)/work/$(PROJECT)-6382.log >> $@ + echo unixsocket $(shell pwd)/work/$(PROJECT)-6382.sock >> $@ echo unixsocketperm 755 >> $@ echo "requirepass foobared" >> $@ echo "user default on #1b58ee375b42e41f0e48ef2ff27d10a5b1f6924a9acdcdba7cae868e7adce6bf ~* +@all" >> $@ @@ -55,7 +57,7 @@ work/redis-6382.conf: echo save \"\" >> $@ # Handled separately because it's the master and all others are slaves -work/redis-6379.conf: +work/$(PROJECT)-6379.conf: @mkdir -p $(@D) echo port 6379 >> $@ @@ -63,18 +65,18 @@ work/redis-6379.conf: echo protected-mode no >> $@ echo bind 0.0.0.0 >> $@ echo notify-keyspace-events Ex >> $@ - echo pidfile $(shell pwd)/work/redis-6379.pid >> $@ - echo logfile $(shell pwd)/work/redis-6379.log >> $@ - echo unixsocket $(shell pwd)/work/redis-6379.sock >> $@ + echo pidfile $(shell pwd)/work/$(PROJECT)-6379.pid >> $@ + echo logfile $(shell pwd)/work/$(PROJECT)-6379.log >> $@ + echo unixsocket $(shell pwd)/work/$(PROJECT)-6379.sock >> $@ echo unixsocketperm 755 >> $@ echo save \"\" >> $@ -work/redis-%.pid: work/redis-%.conf work/redis/bin/redis-server - work/redis/bin/redis-server $< +work/$(PROJECT)-%.pid: work/$(PROJECT)-%.conf work/$(PROJECT)/bin/$(PROJECT)-server + work/$(PROJECT)/bin/$(PROJECT)-server $< -redis-start: work/redis-6379.pid work/redis-6380.pid work/redis-6381.pid work/redis-6382.pid +server-start: work/$(PROJECT)-6379.pid work/$(PROJECT)-6380.pid work/$(PROJECT)-6381.pid work/$(PROJECT)-6382.pid -redis-stop: stop-6379 stop-6380 stop-6381 stop-6382 +server-stop: stop-6379 stop-6380 stop-6381 stop-6382 ########## # Sentinel @@ -110,8 +112,8 @@ work/sentinel-26382.conf: echo sentinel monitor mymaster 127.0.0.1 6382 2 >> $@ echo sentinel auth-pass mymaster foobared >> $@ -work/sentinel-%.pid: work/sentinel-%.conf work/redis-6379.pid work/redis/bin/redis-server - work/redis/bin/redis-server $< --sentinel +work/sentinel-%.pid: work/sentinel-%.conf work/$(PROJECT)-6379.pid work/$(PROJECT)/bin/$(PROJECT)-server + work/$(PROJECT)/bin/$(PROJECT)-server $< --sentinel sentinel-start: work/sentinel-26379.pid work/sentinel-26380.pid work/sentinel-26381.pid work/sentinel-26382.pid @@ -136,20 +138,20 @@ work/cluster-%.conf: echo logfile $(shell pwd)/work/cluster-$*.log >> $@ echo save \"\" >> $@ -work/cluster-%.pid: work/cluster-%.conf work/redis/bin/redis-server - work/redis/bin/redis-server $< & +work/cluster-%.pid: work/cluster-%.conf work/$(PROJECT)/bin/$(PROJECT)-server + work/$(PROJECT)/bin/$(PROJECT)-server $< & cluster-start: work/cluster-7379.pid work/cluster-7380.pid work/cluster-7381.pid work/cluster-7382.pid sleep 1 work/meet-%: - -work/redis/bin/redis-cli -p $* cluster meet 127.0.0.1 7379 + -work/$(PROJECT)/bin/$(PROJECT)-cli -p $* cluster meet 127.0.0.1 7379 # Handled separately because this node is a replica work/meet-7382: - -work/redis/bin/redis-cli -p 7382 cluster meet 127.0.0.1 7379 + -work/$(PROJECT)/bin/$(PROJECT)-cli -p 7382 cluster meet 127.0.0.1 7379 sleep 2 - -work/redis/bin/redis-cli -p 7382 cluster replicate $(shell work/redis/bin/redis-cli -p 7379 cluster myid) + -work/$(PROJECT)/bin/$(PROJECT)-cli -p 7382 cluster replicate $(shell work/$(PROJECT)/bin/$(PROJECT)-cli -p 7379 cluster myid) cluster-meet: work/meet-7380 work/meet-7381 work/meet-7382 sleep 1 @@ -157,9 +159,9 @@ cluster-meet: work/meet-7380 work/meet-7381 work/meet-7382 cluster-stop: stop-7379 stop-7380 stop-7381 stop-7382 cluster-slots: - -work/redis/bin/redis-cli -p 7379 cluster addslots $(shell seq 0 5460) - -work/redis/bin/redis-cli -p 7380 cluster addslots $(shell seq 5461 10922) - -work/redis/bin/redis-cli -p 7381 cluster addslots $(shell seq 10923 16383) + -work/$(PROJECT)/bin/$(PROJECT)-cli -p 7379 cluster addslots $(shell seq 0 5460) + -work/$(PROJECT)/bin/$(PROJECT)-cli -p 7380 cluster addslots $(shell seq 5461 10922) + -work/$(PROJECT)/bin/$(PROJECT)-cli -p 7381 cluster addslots $(shell seq 10923 16383) cluster-init: cluster-start cluster-meet cluster-slots @@ -172,26 +174,26 @@ clean: clobber: rm -rf work -work/redis/bin/redis-cli work/redis/bin/redis-server: - @mkdir -p work/redis +work/$(PROJECT)/bin/$(PROJECT)-cli work/$(PROJECT)/bin/$(PROJECT)-server: + @mkdir -p work/$(PROJECT) - curl -sSL https://github.com/redis/redis/archive/$(REDIS_VERSION).tar.gz | tar xzf - -C work - $(MAKE) -C work/redis-$(REDIS_VERSION) -j - $(MAKE) -C work/redis-$(REDIS_VERSION) PREFIX=$(shell pwd)/work/redis install - rm -rf work/redis-$(REDIS_VERSION) + curl -sSL https://github.com/$(GH_ORG)/$(PROJECT)/archive/refs/tags/$(REDIS_VERSION).tar.gz | tar xzf - -C work + $(MAKE) -C work/$(PROJECT)-$(REDIS_VERSION) -j + $(MAKE) -C work/$(PROJECT)-$(REDIS_VERSION) PREFIX=$(shell pwd)/work/$(PROJECT) install + rm -rf work/$(PROJECT)-$(REDIS_VERSION) -start: redis-start sentinel-start cluster-init +start: server-start sentinel-start cluster-init -stop-%: work/redis/bin/redis-cli - -work/redis/bin/redis-cli -p $* shutdown +stop-%: work/$(PROJECT)/bin/$(PROJECT)-cli + -work/$(PROJECT)/bin/$(PROJECT)-cli -p $* shutdown -stop-6382: work/redis/bin/redis-cli - -work/redis/bin/redis-cli -a foobared -p 6382 shutdown +stop-6382: work/$(PROJECT)/bin/$(PROJECT)-cli + -work/$(PROJECT)/bin/$(PROJECT)-cli -a foobared -p 6382 shutdown -stop-26382: work/redis/bin/redis-cli - -work/redis/bin/redis-cli -a foobared -p 26382 shutdown +stop-26382: work/$(PROJECT)/bin/$(PROJECT)-cli + -work/$(PROJECT)/bin/$(PROJECT)-cli -a foobared -p 26382 shutdown -stop: redis-stop sentinel-stop cluster-stop +stop: server-stop sentinel-stop cluster-stop test: $(MAKE) start From a7d39147cf3f9649b0609a10fc43e5672c732193 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 5 Jun 2024 11:24:41 +0200 Subject: [PATCH 012/187] Add CI stage testing against valkey. Closes #2923 --- Jenkinsfile | 45 ++++++++++++++++++++++++++++-- Makefile | 14 +++++----- ci/openjdk17-redis-6.2/Dockerfile | 5 ++-- ci/openjdk17-redis-7.2/Dockerfile | 6 ++-- ci/openjdk17-valkey-7.2/Dockerfile | 19 +++++++++++++ ci/openjdk21-redis-6.2/Dockerfile | 5 ++-- ci/pipeline.properties | 2 +- 7 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 ci/openjdk17-valkey-7.2/Dockerfile diff --git a/Jenkinsfile b/Jenkinsfile index 4ce7abdfee..5d8009490d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,7 +33,7 @@ pipeline { steps { script { - def image = docker.build("springci/spring-data-with-redis-6.2:${p['java.main.tag']}", "--build-arg BASE=${p['docker.java.main.image']} --build-arg REDIS=${p['docker.redis.6.version']} -f ci/openjdk17-redis-6.2/Dockerfile .") + def image = docker.build("springci/spring-data-with-redis-6.2:${p['java.main.tag']}", "--build-arg BASE=${p['docker.java.main.image']} --build-arg VERSION=${p['docker.redis.6.version']} -f ci/openjdk17-redis-6.2/Dockerfile .") docker.withRegistry(p['docker.registry'], p['docker.credentials']) { image.push() } @@ -53,7 +53,27 @@ pipeline { steps { script { - def image = docker.build("springci/spring-data-with-redis-7.2:${p['java.main.tag']}", "--build-arg BASE=${p['docker.java.main.image']} --build-arg REDIS=${p['docker.redis.7.version']} -f ci/openjdk17-redis-7.2/Dockerfile .") + def image = docker.build("springci/spring-data-with-redis-7.2:${p['java.main.tag']}", "--build-arg BASE=${p['docker.java.main.image']} --build-arg VERSION=${p['docker.redis.7.version']} -f ci/openjdk17-redis-7.2/Dockerfile .") + docker.withRegistry(p['docker.registry'], p['docker.credentials']) { + image.push() + } + } + } + } + stage('Publish JDK 17 + Valkey 7.2 Docker Image') { + when { + anyOf { + changeset "ci/openjdk17-valkey-7.2/Dockerfile" + changeset "Makefile" + changeset "ci/pipeline.properties" + } + } + agent { label 'data' } + options { timeout(time: 20, unit: 'MINUTES') } + + steps { + script { + def image = docker.build("springci/spring-data-with-valkey-7.2:${p['java.main.tag']}", "--build-arg BASE=${p['docker.java.main.image']} --build-arg VERSION=${p['docker.redis.7.version']} -f ci/openjdk17-redis-7.2/Dockerfile .") docker.withRegistry(p['docker.registry'], p['docker.credentials']) { image.push() } @@ -73,7 +93,7 @@ pipeline { steps { script { - def image = docker.build("springci/spring-data-with-redis-6.2:${p['java.next.tag']}", "--build-arg BASE=${p['docker.java.next.image']} --build-arg REDIS=${p['docker.redis.6.version']} -f ci/openjdk21-redis-6.2/Dockerfile .") + def image = docker.build("springci/spring-data-with-redis-6.2:${p['java.next.tag']}", "--build-arg BASE=${p['docker.java.next.image']} --build-arg VERSION=${p['docker.redis.6.version']} -f ci/openjdk21-redis-6.2/Dockerfile .") docker.withRegistry(p['docker.registry'], p['docker.credentials']) { image.push() } @@ -172,6 +192,25 @@ pipeline { } } } + + stage("test: Valkey 7") { + agent { + label 'data' + } + options { timeout(time: 30, unit: 'MINUTES') } + environment { + ARTIFACTORY = credentials("${p['artifactory.credentials']}") + DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") + } + steps { + script { + docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-valkey-7.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + } + } + } + } } } diff --git a/Makefile b/Makefile index 7d58ad6575..304a2648ba 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -REDIS_VERSION:=7.2.5 +VERSION:=7.2.5 PROJECT?=redis GH_ORG?=redis SPRING_PROFILE?=ci @@ -177,10 +177,10 @@ clobber: work/$(PROJECT)/bin/$(PROJECT)-cli work/$(PROJECT)/bin/$(PROJECT)-server: @mkdir -p work/$(PROJECT) - curl -sSL https://github.com/$(GH_ORG)/$(PROJECT)/archive/refs/tags/$(REDIS_VERSION).tar.gz | tar xzf - -C work - $(MAKE) -C work/$(PROJECT)-$(REDIS_VERSION) -j - $(MAKE) -C work/$(PROJECT)-$(REDIS_VERSION) PREFIX=$(shell pwd)/work/$(PROJECT) install - rm -rf work/$(PROJECT)-$(REDIS_VERSION) + curl -sSL https://github.com/$(GH_ORG)/$(PROJECT)/archive/refs/tags/$(VERSION).tar.gz | tar xzf - -C work + $(MAKE) -C work/$(PROJECT)-$(VERSION) -j + $(MAKE) -C work/$(PROJECT)-$(VERSION) PREFIX=$(shell pwd)/work/$(PROJECT) install + rm -rf work/$(PROJECT)-$(VERSION) start: server-start sentinel-start cluster-init @@ -198,14 +198,14 @@ stop: server-stop sentinel-stop cluster-stop test: $(MAKE) start sleep 1 - ./mvnw clean test -U -P$(SPRING_PROFILE) -Dredis.server.version=$(REDIS_VERSION) || (echo "maven failed $$?"; exit 1) + ./mvnw clean test -U -P$(SPRING_PROFILE) || (echo "maven failed $$?"; exit 1) $(MAKE) stop $(MAKE) clean all-tests: $(MAKE) start sleep 1 - ./mvnw clean test -U -DrunLongTests=true -P$(SPRING_PROFILE) -Dredis.server.version=$(REDIS_VERSION) || (echo "maven failed $$?"; exit 1) + ./mvnw clean test -U -DrunLongTests=true -P$(SPRING_PROFILE) || (echo "maven failed $$?"; exit 1) $(MAKE) stop $(MAKE) clean diff --git a/ci/openjdk17-redis-6.2/Dockerfile b/ci/openjdk17-redis-6.2/Dockerfile index b983e39fb0..d20826b6cb 100644 --- a/ci/openjdk17-redis-6.2/Dockerfile +++ b/ci/openjdk17-redis-6.2/Dockerfile @@ -1,7 +1,8 @@ ARG BASE FROM ${BASE} # Any ARG statements before FROM are cleared. -ARG REDIS +ARG VERSION +ENV VERSION=${VERSION} # Copy Spring Data Redis's Makefile into the container COPY ./Makefile / @@ -10,7 +11,7 @@ RUN set -eux; \ # sed -i -e 's/http/https/g' /etc/apt/sources.list ; \ apt-get update ; \ apt-get install -y build-essential ; \ - make work/redis/bin/redis-cli work/redis/bin/redis-server REDIS_VERSION=${REDIS}; \ + make work/redis/bin/redis-cli work/redis/bin/redis-server VERSION=${VERSION}; \ chmod -R o+rw work; \ apt-get clean; \ rm -rf /var/lib/apt/lists/*; diff --git a/ci/openjdk17-redis-7.2/Dockerfile b/ci/openjdk17-redis-7.2/Dockerfile index 6251772188..d20826b6cb 100644 --- a/ci/openjdk17-redis-7.2/Dockerfile +++ b/ci/openjdk17-redis-7.2/Dockerfile @@ -1,8 +1,8 @@ ARG BASE FROM ${BASE} # Any ARG statements before FROM are cleared. -ARG REDIS -ENV REDIS_VERSION=${REDIS} +ARG VERSION +ENV VERSION=${VERSION} # Copy Spring Data Redis's Makefile into the container COPY ./Makefile / @@ -11,7 +11,7 @@ RUN set -eux; \ # sed -i -e 's/http/https/g' /etc/apt/sources.list ; \ apt-get update ; \ apt-get install -y build-essential ; \ - make work/redis/bin/redis-cli work/redis/bin/redis-server REDIS_VERSION=${REDIS}; \ + make work/redis/bin/redis-cli work/redis/bin/redis-server VERSION=${VERSION}; \ chmod -R o+rw work; \ apt-get clean; \ rm -rf /var/lib/apt/lists/*; diff --git a/ci/openjdk17-valkey-7.2/Dockerfile b/ci/openjdk17-valkey-7.2/Dockerfile new file mode 100644 index 0000000000..1b89b923ae --- /dev/null +++ b/ci/openjdk17-valkey-7.2/Dockerfile @@ -0,0 +1,19 @@ +ARG BASE +FROM ${BASE} +# Any ARG statements before FROM are cleared. +ARG VERSION +ENV VERSION=${VERSION} +ENV PROJECT=valkey +ENV GH_ORG=valkey-io + +# Copy Spring Data Redis's Makefile into the container +COPY ./Makefile / + +RUN set -eux; \ +# sed -i -e 's/http/https/g' /etc/apt/sources.list ; \ + apt-get update ; \ + apt-get install -y build-essential ; \ + make work/valkey/bin/valkey-cli work/valkey/bin/valkey-server VERSION=${VERSION}; \ + chmod -R o+rw work; \ + apt-get clean; \ + rm -rf /var/lib/apt/lists/*; diff --git a/ci/openjdk21-redis-6.2/Dockerfile b/ci/openjdk21-redis-6.2/Dockerfile index b983e39fb0..d20826b6cb 100644 --- a/ci/openjdk21-redis-6.2/Dockerfile +++ b/ci/openjdk21-redis-6.2/Dockerfile @@ -1,7 +1,8 @@ ARG BASE FROM ${BASE} # Any ARG statements before FROM are cleared. -ARG REDIS +ARG VERSION +ENV VERSION=${VERSION} # Copy Spring Data Redis's Makefile into the container COPY ./Makefile / @@ -10,7 +11,7 @@ RUN set -eux; \ # sed -i -e 's/http/https/g' /etc/apt/sources.list ; \ apt-get update ; \ apt-get install -y build-essential ; \ - make work/redis/bin/redis-cli work/redis/bin/redis-server REDIS_VERSION=${REDIS}; \ + make work/redis/bin/redis-cli work/redis/bin/redis-server VERSION=${VERSION}; \ chmod -R o+rw work; \ apt-get clean; \ rm -rf /var/lib/apt/lists/*; diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 6ff93899b4..ed67f6703d 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -14,7 +14,7 @@ docker.mongodb.7.0.version=7.0.2 # Supported versions of Redis docker.redis.6.version=6.2.13 -docker.redis.7.version=7.2.4 +docker.redis.7.version=7.2.5 # Supported versions of Cassandra docker.cassandra.3.version=3.11.16 From 3fed336179b134b1bcf12843fbdfdb39d4cf926f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 20 Jun 2024 14:07:12 +0200 Subject: [PATCH 013/187] Switch to Broadcom docker proxy. Closes #2929 --- Jenkinsfile | 58 +++++++++++++++++++++++++----------------- ci/pipeline.properties | 6 +++-- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5d8009490d..3ce5a5178f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -122,8 +122,10 @@ pipeline { } steps { script { - docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-redis-6.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { - sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { + docker.image("springci/spring-data-with-redis-6.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + } } } } @@ -150,8 +152,10 @@ pipeline { } steps { script { - docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-redis-6.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { - sh "PROFILE=runtimehints LONG_TESTS=false JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { + docker.image("springci/spring-data-with-redis-6.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + sh "PROFILE=runtimehints LONG_TESTS=false JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + } } } } @@ -168,8 +172,10 @@ pipeline { } steps { script { - docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-redis-6.2:${p['java.next.tag']}").inside('-v $HOME:/tmp/jenkins-home') { - sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { + docker.image("springci/spring-data-with-redis-6.2:${p['java.next.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + } } } } @@ -186,8 +192,10 @@ pipeline { } steps { script { - docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-redis-7.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { - sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { + docker.image("springci/spring-data-with-redis-7.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + } } } } @@ -205,8 +213,10 @@ pipeline { } steps { script { - docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-valkey-7.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { - sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { + docker.image("springci/spring-data-with-valkey-7.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" + } } } } @@ -235,19 +245,21 @@ pipeline { steps { script { - docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { - sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' + - "DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} " + - "DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} " + - "GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} " + - "./mvnw -s settings.xml -Pci,artifactory " + - "-Dartifactory.server=${p['artifactory.url']} " + - "-Dartifactory.username=${ARTIFACTORY_USR} " + - "-Dartifactory.password=${ARTIFACTORY_PSW} " + - "-Dartifactory.staging-repository=${p['artifactory.repository.snapshot']} " + - "-Dartifactory.build-name=spring-data-redis " + - "-Dartifactory.build-number=spring-data-redis-${BRANCH_NAME}-build-${BUILD_NUMBER} " + - "-Dmaven.test.skip=true clean deploy -U -B" + docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { + docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { + sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' + + "DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} " + + "DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} " + + "GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} " + + "./mvnw -s settings.xml -Pci,artifactory " + + "-Dartifactory.server=${p['artifactory.url']} " + + "-Dartifactory.username=${ARTIFACTORY_USR} " + + "-Dartifactory.password=${ARTIFACTORY_PSW} " + + "-Dartifactory.staging-repository=${p['artifactory.repository.snapshot']} " + + "-Dartifactory.build-name=spring-data-redis " + + "-Dartifactory.build-number=spring-data-redis-${BRANCH_NAME}-build-${BUILD_NUMBER} " + + "-Dmaven.test.skip=true clean deploy -U -B" + } } } } diff --git a/ci/pipeline.properties b/ci/pipeline.properties index ed67f6703d..f024b1feb8 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -3,8 +3,8 @@ java.main.tag=17.0.9_9-jdk-focal java.next.tag=21.0.1_12-jdk-jammy # Docker container images - standard -docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} -docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag} +docker.java.main.image=library/eclipse-temurin:${java.main.tag} +docker.java.next.image=library/eclipse-temurin:${java.next.tag} # Supported versions of MongoDB docker.mongodb.4.4.version=4.4.25 @@ -26,6 +26,8 @@ docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock - # Credentials docker.registry= docker.credentials=hub.docker.com-springbuildmaster +docker.proxy.registry=https://docker-hub.usw1.packages.broadcom.com +docker.proxy.credentials=usw1_packages_broadcom_com-jenkins-token artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c artifactory.url=https://repo.spring.io artifactory.repository.snapshot=libs-snapshot-local From 6ce2896ba1cfd7a15b9bc37d404371fba304adcc Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 20 Jun 2024 14:07:27 +0200 Subject: [PATCH 014/187] Remove Slack notification. See #2929 --- Jenkinsfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3ce5a5178f..d6b8b4fafc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -269,10 +269,6 @@ pipeline { post { changed { script { - slackSend( - color: (currentBuild.currentResult == 'SUCCESS') ? 'good' : 'danger', - channel: '#spring-data-dev', - message: "${currentBuild.fullDisplayName} - `${currentBuild.currentResult}`\n${env.BUILD_URL}") emailext( subject: "[${currentBuild.fullDisplayName}] ${currentBuild.currentResult}", mimeType: 'text/html', From 4203a5bb671655e78562e2b607a2c52cb8cf055e Mon Sep 17 00:00:00 2001 From: Seungrae Date: Sun, 30 Jun 2024 21:57:36 +0900 Subject: [PATCH 015/187] Fix misspelled method name in RedisPersistentEntity. Resolves: #2933 Closes: #2935 --- .../springframework/data/redis/core/RedisKeyValueAdapter.java | 2 +- .../data/redis/core/mapping/BasicRedisPersistentEntity.java | 2 +- .../data/redis/core/mapping/RedisPersistentEntity.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java index 6aaabbe156..bbd96434a0 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java @@ -592,7 +592,7 @@ private T readBackTimeToLiveIfSet(@Nullable byte[] key, @Nullable T target) RedisPersistentEntity entity = this.converter.getMappingContext().getRequiredPersistentEntity(target.getClass()); - if (entity.hasExplictTimeToLiveProperty()) { + if (entity.hasExplicitTimeToLiveProperty()) { RedisPersistentProperty ttlProperty = entity.getExplicitTimeToLiveProperty(); diff --git a/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java b/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java index 7c68335874..f7346fb603 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java @@ -58,7 +58,7 @@ public TimeToLiveAccessor getTimeToLiveAccessor() { } @Override - public boolean hasExplictTimeToLiveProperty() { + public boolean hasExplicitTimeToLiveProperty() { return getExplicitTimeToLiveProperty() != null; } diff --git a/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java b/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java index a31eca7895..6247bb47bd 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java @@ -41,7 +41,7 @@ public interface RedisPersistentEntity extends KeyValuePersistentEntity Date: Tue, 2 Jul 2024 08:16:25 +0200 Subject: [PATCH 016/187] Deprecate method instead of removing it. Original Pull Request: #2935 --- .../core/mapping/BasicRedisPersistentEntity.java | 5 ----- .../redis/core/mapping/RedisPersistentEntity.java | 14 +++++++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java b/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java index f7346fb603..14bbc8ee6a 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java @@ -57,11 +57,6 @@ public TimeToLiveAccessor getTimeToLiveAccessor() { return this.timeToLiveAccessor; } - @Override - public boolean hasExplicitTimeToLiveProperty() { - return getExplicitTimeToLiveProperty() != null; - } - @Override @Nullable public RedisPersistentProperty getExplicitTimeToLiveProperty() { diff --git a/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java b/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java index 6247bb47bd..34ba7db186 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java @@ -40,8 +40,20 @@ public interface RedisPersistentEntity extends KeyValuePersistentEntity Date: Wed, 31 Jul 2024 14:29:50 +0200 Subject: [PATCH 017/187] Bundle Javadoc with Antora documentation site. Closes #2950 --- .gitignore | 1 - package.json | 10 ++++ pom.xml | 2 +- src/main/antora/antora-playbook.yml | 8 ++- src/main/antora/antora.yml | 5 ++ src/main/antora/modules/ROOT/nav.adoc | 4 +- .../ROOT/pages/redis/connection-modes.adoc | 10 ++-- .../modules/ROOT/pages/redis/drivers.adoc | 2 +- .../ROOT/pages/redis/getting-started.adoc | 2 +- .../ROOT/pages/redis/hash-mappers.adoc | 14 ++--- .../modules/ROOT/pages/redis/pubsub.adoc | 18 +++---- .../modules/ROOT/pages/redis/redis-cache.adoc | 8 +-- .../redis-repositories/cdi-integration.adoc | 6 +-- .../redis/redis-repositories/cluster.adoc | 2 - .../redis/redis-repositories/expirations.adoc | 8 +-- .../ROOT/pages/redis/redis-streams.adoc | 6 +-- .../modules/ROOT/pages/redis/scripting.adoc | 8 +-- .../ROOT/pages/redis/support-classes.adoc | 6 +-- .../modules/ROOT/pages/redis/template.adoc | 54 +++++++++---------- .../ROOT/pages/redis/transactions.adoc | 4 +- 20 files changed, 95 insertions(+), 83 deletions(-) create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 419a1317bd..b34bc1769a 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,5 @@ work build/ node_modules node -package.json package-lock.json .mvn/.gradle-enterprise diff --git a/package.json b/package.json new file mode 100644 index 0000000000..4689506b3f --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "dependencies": { + "antora": "3.2.0-alpha.6", + "@antora/atlas-extension": "1.0.0-alpha.2", + "@antora/collector-extension": "1.0.0-alpha.7", + "@asciidoctor/tabs": "1.0.0-beta.6", + "@springio/antora-extensions": "1.13.0", + "@springio/asciidoctor-extensions": "1.0.0-alpha.11" + } +} diff --git a/pom.xml b/pom.xml index d129d27346..f39b1fe04c 100644 --- a/pom.xml +++ b/pom.xml @@ -367,7 +367,7 @@ - io.spring.maven.antora + org.antora antora-maven-plugin diff --git a/src/main/antora/antora-playbook.yml b/src/main/antora/antora-playbook.yml index b68f34c9b8..b90d6ab326 100644 --- a/src/main/antora/antora-playbook.yml +++ b/src/main/antora/antora-playbook.yml @@ -3,8 +3,7 @@ # The purpose of this Antora playbook is to build the docs in the current branch. antora: extensions: - - '@antora/collector-extension' - - require: '@springio/antora-extensions/root-component-extension' + - require: '@springio/antora-extensions' root_component_name: 'data-redis' site: title: Spring Data Redis @@ -22,13 +21,12 @@ content: start_path: src/main/antora asciidoc: attributes: - page-pagination: '' hide-uri-scheme: '@' tabs-sync-option: '@' - chomp: 'all' extensions: - '@asciidoctor/tabs' - '@springio/asciidoctor-extensions' + - '@springio/asciidoctor-extensions/javadoc-extension' sourcemap: true urls: latest_version_segment: '' @@ -38,5 +36,5 @@ runtime: format: pretty ui: bundle: - url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.3.5/ui-bundle.zip + url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.16/ui-bundle.zip snapshot: true diff --git a/src/main/antora/antora.yml b/src/main/antora/antora.yml index 3b978752a0..f2cc6bda15 100644 --- a/src/main/antora/antora.yml +++ b/src/main/antora/antora.yml @@ -10,3 +10,8 @@ ext: local: true scan: dir: target/classes/ + - run: + command: ./mvnw package -Pdistribute + local: true + scan: + dir: target/antora diff --git a/src/main/antora/modules/ROOT/nav.adoc b/src/main/antora/modules/ROOT/nav.adoc index 11f202b10c..7cc4db98ce 100644 --- a/src/main/antora/modules/ROOT/nav.adoc +++ b/src/main/antora/modules/ROOT/nav.adoc @@ -44,4 +44,6 @@ * xref:appendix.adoc[] -* https://github.com/spring-projects/spring-data-commons/wiki[Wiki] + +* xref:attachment$api/java/index.html[Javadoc,role=link-external,window=_blank] +* https://github.com/spring-projects/spring-data-commons/wiki[Wiki,role=link-external,window=_blank] diff --git a/src/main/antora/modules/ROOT/pages/redis/connection-modes.adoc b/src/main/antora/modules/ROOT/pages/redis/connection-modes.adoc index 062eb33d06..2a8f11623f 100644 --- a/src/main/antora/modules/ROOT/pages/redis/connection-modes.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/connection-modes.adoc @@ -9,7 +9,7 @@ Each mode of operation requires specific configuration that is explained in the The easiest way to get started is by using Redis Standalone with a single Redis server, -Configure `LettuceClientConfiguration` or `JedisConnectionFactory`, as shown in the following example: +Configure javadoc:org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory[] or javadoc:org.springframework.data.redis.connection.jedis.JedisConnectionFactory[], as shown in the following example: [source,java] ---- @@ -60,12 +60,12 @@ class WriteToMasterReadFromReplicaConfiguration { } ---- -TIP: For environments reporting non-public addresses through the `INFO` command (for example, when using AWS), use `RedisStaticMasterReplicaConfiguration` instead of `RedisStandaloneConfiguration`. Please note that `RedisStaticMasterReplicaConfiguration` does not support Pub/Sub because of missing Pub/Sub message propagation across individual servers. +TIP: For environments reporting non-public addresses through the `INFO` command (for example, when using AWS), use javadoc:org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration[] instead of javadoc:org.springframework.data.redis.connection.RedisStandaloneConfiguration[]. Please note that `RedisStaticMasterReplicaConfiguration` does not support Pub/Sub because of missing Pub/Sub message propagation across individual servers. [[redis:sentinel]] == Redis Sentinel -For dealing with high-availability Redis, Spring Data Redis has support for https://redis.io/topics/sentinel[Redis Sentinel], using `RedisSentinelConfiguration`, as shown in the following example: +For dealing with high-availability Redis, Spring Data Redis has support for https://redis.io/topics/sentinel[Redis Sentinel], using javadoc:org.springframework.data.redis.connection.RedisSentinelConfiguration[], as shown in the following example: [source,java] ---- @@ -113,8 +113,8 @@ Sometimes, direct interaction with one of the Sentinels is required. Using `Redi [[cluster.enable]] == Redis Cluster -xref:redis/cluster.adoc[Cluster support] is based on the same building blocks as non-clustered communication. `RedisClusterConnection`, an extension to `RedisConnection`, handles the communication with the Redis Cluster and translates errors into the Spring DAO exception hierarchy. -`RedisClusterConnection` instances are created with the `RedisConnectionFactory`, which has to be set up with the associated `RedisClusterConfiguration`, as shown in the following example: +xref:redis/cluster.adoc[Cluster support] is based on the same building blocks as non-clustered communication. javadoc:org.springframework.data.redis.connection.RedisClusterConnection[], an extension to `RedisConnection`, handles the communication with the Redis Cluster and translates errors into the Spring DAO exception hierarchy. +`RedisClusterConnection` instances are created with the `RedisConnectionFactory`, which has to be set up with the associated javadoc:org.springframework.data.redis.connection.RedisClusterConfiguration[], as shown in the following example: .Sample RedisConnectionFactory Configuration for Redis Cluster ==== diff --git a/src/main/antora/modules/ROOT/pages/redis/drivers.adoc b/src/main/antora/modules/ROOT/pages/redis/drivers.adoc index 2f649f3166..52570144d5 100644 --- a/src/main/antora/modules/ROOT/pages/redis/drivers.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/drivers.adoc @@ -154,7 +154,7 @@ public LettuceConnectionFactory lettuceConnectionFactory() { } ---- -For more detailed client configuration tweaks, see https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.html[`LettuceClientConfiguration`]. +For more detailed client configuration tweaks, see javadoc:org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration[]. Lettuce integrates with Netty's https://netty.io/wiki/native-transports.html[native transports], letting you use Unix domain sockets to communicate with Redis. Make sure to include the appropriate native transport dependencies that match your runtime environment. diff --git a/src/main/antora/modules/ROOT/pages/redis/getting-started.adoc b/src/main/antora/modules/ROOT/pages/redis/getting-started.adoc index 4c16e17e98..3a803ad85b 100644 --- a/src/main/antora/modules/ROOT/pages/redis/getting-started.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/getting-started.adoc @@ -37,5 +37,5 @@ include::example$examples/ReactiveRedisApplication.java[tags=file] Even in this simple example, there are a few notable things to point out: -* You can create an instance of `RedisTemplate` (or `ReactiveRedisTemplate` for reactive usage) with a `RedisConnectionFactory`. Connection factories are an abstraction on top of the supported drivers. +* You can create an instance of javadoc:org.springframework.data.redis.core.RedisTemplate[] (or javadoc:org.springframework.data.redis.core.ReactiveRedisTemplate[]for reactive usage) with a javadoc:org.springframework.data.redis.connection.RedisConnectionFactory[]. Connection factories are an abstraction on top of the supported drivers. * There's no single way to use Redis as it comes with support for a wide range of data structures such as plain keys ("strings"), lists, sets, sorted sets, streams, hashes and so on. diff --git a/src/main/antora/modules/ROOT/pages/redis/hash-mappers.adoc b/src/main/antora/modules/ROOT/pages/redis/hash-mappers.adoc index 2ecbc321b7..334a2fd515 100644 --- a/src/main/antora/modules/ROOT/pages/redis/hash-mappers.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/hash-mappers.adoc @@ -1,21 +1,21 @@ [[redis.hashmappers.root]] = Hash Mapping -Data can be stored by using various data structures within Redis. `Jackson2JsonRedisSerializer` can convert objects in https://en.wikipedia.org/wiki/JSON[JSON] format. Ideally, JSON can be stored as a value by using plain keys. You can achieve a more sophisticated mapping of structured objects by using Redis hashes. Spring Data Redis offers various strategies for mapping data to hashes (depending on the use case): +Data can be stored by using various data structures within Redis. javadoc:org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer[] can convert objects in https://en.wikipedia.org/wiki/JSON[JSON] format. Ideally, JSON can be stored as a value by using plain keys. You can achieve a more sophisticated mapping of structured objects by using Redis hashes. Spring Data Redis offers various strategies for mapping data to hashes (depending on the use case): -* Direct mapping, by using `HashOperations` and a xref:redis.adoc#redis:serializer[serializer] +* Direct mapping, by using javadoc:org.springframework.data.redis.core.HashOperations[] and a xref:redis.adoc#redis:serializer[serializer] * Using xref:repositories.adoc[Redis Repositories] -* Using `HashMapper` and `HashOperations` +* Using javadoc:org.springframework.data.redis.hash.HashMapper[] and javadoc:org.springframework.data.redis.core.HashOperations[] [[redis.hashmappers.mappers]] == Hash Mappers -Hash mappers are converters of map objects to a `Map` and back. `HashMapper` is intended for using with Redis Hashes. +Hash mappers are converters of map objects to a `Map` and back. javadoc:org.springframework.data.redis.hash.HashMapper[] is intended for using with Redis Hashes. Multiple implementations are available: -* `BeanUtilsHashMapper` using Spring's {spring-framework-javadoc}/org/springframework/beans/BeanUtils.html[BeanUtils]. -* `ObjectHashMapper` using xref:redis/redis-repositories/mapping.adoc[Object-to-Hash Mapping]. +* javadoc:org.springframework.data.redis.hash.BeanUtilsHashMapper[] using Spring's {spring-framework-javadoc}/org/springframework/beans/BeanUtils.html[BeanUtils]. +* javadoc:org.springframework.data.redis.hash.ObjectHashMapper[] using xref:redis/redis-repositories/mapping.adoc[Object-to-Hash Mapping]. * <> using https://github.com/FasterXML/jackson[FasterXML Jackson]. The following example shows one way to implement hash mapping: @@ -53,7 +53,7 @@ public class HashMapping { [[redis.hashmappers.jackson2]] === Jackson2HashMapper -`Jackson2HashMapper` provides Redis Hash mapping for domain objects by using https://github.com/FasterXML/jackson[FasterXML Jackson]. +javadoc:org.springframework.data.redis.hash.Jackson2HashMapper[] provides Redis Hash mapping for domain objects by using https://github.com/FasterXML/jackson[FasterXML Jackson]. `Jackson2HashMapper` can map top-level properties as Hash field names and, optionally, flatten the structure. Simple types map to simple values. Complex types (nested objects, collections, maps, and so on) are represented as nested JSON. diff --git a/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc b/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc index 0fdc427c58..6539ca127c 100644 --- a/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc @@ -66,13 +66,13 @@ In order to subscribe to messages, one needs to implement the `MessageListener` [[redis:pubsub:subscribe:containers]] === Message Listener Containers -Due to its blocking nature, low-level subscription is not attractive, as it requires connection and thread management for every single listener. To alleviate this problem, Spring Data offers `RedisMessageListenerContainer`, which does all the heavy lifting. If you are familiar with EJB and JMS, you should find the concepts familiar, as it is designed to be as close as possible to the support in Spring Framework and its message-driven POJOs (MDPs). +Due to its blocking nature, low-level subscription is not attractive, as it requires connection and thread management for every single listener. To alleviate this problem, Spring Data offers javadoc:org.springframework.data.redis.listener.RedisMessageListenerContainer[], which does all the heavy lifting. If you are familiar with EJB and JMS, you should find the concepts familiar, as it is designed to be as close as possible to the support in Spring Framework and its message-driven POJOs (MDPs). -`RedisMessageListenerContainer` acts as a message listener container. It is used to receive messages from a Redis channel and drive the `MessageListener` instances that are injected into it. The listener container is responsible for all threading of message reception and dispatches into the listener for processing. A message listener container is the intermediary between an MDP and a messaging provider and takes care of registering to receive messages, resource acquisition and release, exception conversion, and the like. This lets you as an application developer write the (possibly complex) business logic associated with receiving a message (and reacting to it) and delegates boilerplate Redis infrastructure concerns to the framework. +javadoc:org.springframework.data.redis.listener.RedisMessageListenerContainer[] acts as a message listener container. It is used to receive messages from a Redis channel and drive the javadoc:org.springframework.data.redis.connection.MessageListener[] instances that are injected into it. The listener container is responsible for all threading of message reception and dispatches into the listener for processing. A message listener container is the intermediary between an MDP and a messaging provider and takes care of registering to receive messages, resource acquisition and release, exception conversion, and the like. This lets you as an application developer write the (possibly complex) business logic associated with receiving a message (and reacting to it) and delegates boilerplate Redis infrastructure concerns to the framework. -A `MessageListener` can additionally implement `SubscriptionListener` to receive notifications upon subscription/unsubscribe confirmation. Listening to subscription notifications can be useful when synchronizing invocations. +A javadoc:org.springframework.data.redis.connection.MessageListener[] can additionally implement javadoc:org.springframework.data.redis.connection.SubscriptionListener[] to receive notifications upon subscription/unsubscribe confirmation. Listening to subscription notifications can be useful when synchronizing invocations. -Furthermore, to minimize the application footprint, `RedisMessageListenerContainer` lets one connection and one thread be shared by multiple listeners even though they do not share a subscription. Thus, no matter how many listeners or channels an application tracks, the runtime cost remains the same throughout its lifetime. Moreover, the container allows runtime configuration changes so that you can add or remove listeners while an application is running without the need for a restart. Additionally, the container uses a lazy subscription approach, using a `RedisConnection` only when needed. If all the listeners are unsubscribed, cleanup is automatically performed, and the thread is released. +Furthermore, to minimize the application footprint, javadoc:org.springframework.data.redis.listener.RedisMessageListenerContainer[] lets one connection and one thread be shared by multiple listeners even though they do not share a subscription. Thus, no matter how many listeners or channels an application tracks, the runtime cost remains the same throughout its lifetime. Moreover, the container allows runtime configuration changes so that you can add or remove listeners while an application is running without the need for a restart. Additionally, the container uses a lazy subscription approach, using a `RedisConnection` only when needed. If all the listeners are unsubscribed, cleanup is automatically performed, and the thread is released. To help with the asynchronous nature of messages, the container requires a `java.util.concurrent.Executor` (or Spring's `TaskExecutor`) for dispatching the messages. Depending on the load, the number of listeners, or the runtime environment, you should change or tweak the executor to better serve your needs. In particular, in managed environments (such as app servers), it is highly recommended to pick a proper `TaskExecutor` to take advantage of its runtime. @@ -80,7 +80,7 @@ To help with the asynchronous nature of messages, the container requires a `java [[redis:pubsub:subscribe:adapter]] === The MessageListenerAdapter -The `MessageListenerAdapter` class is the final component in Spring's asynchronous messaging support. In a nutshell, it lets you expose almost *any* class as a MDP (though there are some constraints). +The javadoc:org.springframework.data.redis.listener.adapter.MessageListenerAdapter[] class is the final component in Spring's asynchronous messaging support. In a nutshell, it lets you expose almost *any* class as a MDP (though there are some constraints). Consider the following interface definition: @@ -96,7 +96,7 @@ public interface MessageDelegate { } ---- -Notice that, although the interface does not extend the `MessageListener` interface, it can still be used as a MDP by using the `MessageListenerAdapter` class. Notice also how the various message handling methods are strongly typed according to the *contents* of the various `Message` types that they can receive and handle. In addition, the channel or pattern to which a message is sent can be passed in to the method as the second argument of type `String`: +Notice that, although the interface does not extend the `MessageListener` interface, it can still be used as a MDP by using the javadoc:org.springframework.data.redis.listener.adapter.MessageListenerAdapter[] class. Notice also how the various message handling methods are strongly typed according to the *contents* of the various `Message` types that they can receive and handle. In addition, the channel or pattern to which a message is sent can be passed in to the method as the second argument of type `String`: [source,java] ---- @@ -105,7 +105,7 @@ public class DefaultMessageDelegate implements MessageDelegate { } ---- -Notice how the above implementation of the `MessageDelegate` interface (the above `DefaultMessageDelegate` class) has *no* Redis dependencies at all. It truly is a POJO that we make into an MDP with the following configuration: + Notice how the above implementation of the `MessageDelegate` interface (the above `DefaultMessageDelegate` class) has *no* Redis dependencies at all. It truly is a POJO that we make into an MDP with the following configuration: [tabs] ====== @@ -193,7 +193,7 @@ Each time a message is received, the adapter automatically and transparently per [[redis:reactive:pubsub:subscribe:containers]] == Reactive Message Listener Container -Spring Data offers `ReactiveRedisMessageListenerContainer` which does all the heavy lifting of conversion and subscription state management on behalf of the user. +Spring Data offers javadoc:org.springframework.data.redis.listener.ReactiveRedisMessageListenerContainer[] which does all the heavy lifting of conversion and subscription state management on behalf of the user. The message listener container itself does not require external threading resources. It uses the driver threads to publish messages. @@ -223,7 +223,7 @@ stream.doOnNext(inner -> // notification hook when Redis subscriptions are synch [[redis:reactive:pubsub:subscribe:template]] === Subscribing via template API -As mentioned above you can directly use `ReactiveRedisTemplate` to subscribe to channels / patterns. This approach +As mentioned above you can directly use javadoc:org.springframework.data.redis.core.ReactiveRedisTemplate[] to subscribe to channels / patterns. This approach offers a straight forward, though limited solution as you lose the option to add subscriptions after the initial ones. Nevertheless you still can control the message stream via the returned `Flux` using eg. `take(Duration)`. When done reading, on error or cancellation all bound resources are freed again. diff --git a/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc b/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc index c965f352a9..3726bb0680 100644 --- a/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc @@ -2,7 +2,7 @@ = Redis Cache Spring Data Redis provides an implementation of Spring Framework's {spring-framework-docs}/integration.html#cache[Cache Abstraction] in the `org.springframework.data.redis.cache` package. -To use Redis as a backing implementation, add `RedisCacheManager` to your configuration, as follows: +To use Redis as a backing implementation, add javadoc:org.springframework.data.redis.cache.RedisCacheManager[] to your configuration, as follows: [source,java] ---- @@ -12,7 +12,7 @@ public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) } ---- -`RedisCacheManager` behavior can be configured with `RedisCacheManagerBuilder`, letting you set the default `RedisCacheConfiguration`, transaction behavior, and predefined caches. +`RedisCacheManager` behavior can be configured with javadoc:org.springframework.data.redis.cache.RedisCacheManager$RedisCacheManagerBuilder[], letting you set the default javadoc:org.springframework.data.redis.cache.RedisCacheManager[], transaction behavior, and predefined caches. [source,java] ---- @@ -26,7 +26,7 @@ RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory) As shown in the preceding example, `RedisCacheManager` allows custom configuration on a per-cache basis. -The behavior of `RedisCache` created by `RedisCacheManager` is defined with `RedisCacheConfiguration`. +The behavior of javadoc:org.springframework.data.redis.cache.RedisCache[] created by javadoc:org.springframework.data.redis.cache.RedisCacheManager[] is defined with `RedisCacheConfiguration`. The configuration lets you set key expiration times, prefixes, and `RedisSerializer` implementations for converting to and from the binary storage format, as shown in the following example: [source,java] @@ -36,7 +36,7 @@ RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCach .disableCachingNullValues(); ---- -`RedisCacheManager` defaults to a lock-free `RedisCacheWriter` for reading and writing binary values. +javadoc:org.springframework.data.redis.cache.RedisCacheManager[] defaults to a lock-free javadoc:org.springframework.data.redis.cache.RedisCacheWriter[] for reading and writing binary values. Lock-free caching improves throughput. The lack of entry locking can lead to overlapping, non-atomic commands for the `Cache` `putIfAbsent` and `clean` operations, as those require multiple commands to be sent to Redis. The locking counterpart prevents command overlap by setting an explicit lock key and checking against presence of this key, which leads to additional requests and potential command wait times. diff --git a/src/main/antora/modules/ROOT/pages/redis/redis-repositories/cdi-integration.adoc b/src/main/antora/modules/ROOT/pages/redis/redis-repositories/cdi-integration.adoc index d841506565..bd36cb1c59 100644 --- a/src/main/antora/modules/ROOT/pages/redis/redis-repositories/cdi-integration.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/redis-repositories/cdi-integration.adoc @@ -6,7 +6,7 @@ Spring offers sophisticated for creating bean instances. Spring Data Redis ships with a custom CDI extension that lets you use the repository abstraction in CDI environments. The extension is part of the JAR, so, to activate it, drop the Spring Data Redis JAR into your classpath. -You can then set up the infrastructure by implementing a CDI Producer for the `RedisConnectionFactory` and `RedisOperations`, as shown in the following example: +You can then set up the infrastructure by implementing a CDI Producer for the javadoc:org.springframework.data.redis.connection.RedisConnectionFactory[] and javadoc:org.springframework.data.redis.core.RedisOperations[], as shown in the following example: [source,java] ---- @@ -62,7 +62,7 @@ class RepositoryClient { } ---- -A Redis Repository requires `RedisKeyValueAdapter` and `RedisKeyValueTemplate` instances. +A Redis Repository requires javadoc:org.springframework.data.redis.core.RedisKeyValueAdapter[] and javadoc:org.springframework.data.redis.core.RedisKeyValueTemplate[] instances. These beans are created and managed by the Spring Data CDI extension if no provided beans are found. -You can, however, supply your own beans to configure the specific properties of `RedisKeyValueAdapter` and `RedisKeyValueTemplate`. +You can, however, supply your own beans to configure the specific properties of javadoc:org.springframework.data.redis.core.RedisKeyValueAdapter[] and javadoc:org.springframework.data.redis.core.RedisKeyValueTemplate[]. diff --git a/src/main/antora/modules/ROOT/pages/redis/redis-repositories/cluster.adoc b/src/main/antora/modules/ROOT/pages/redis/redis-repositories/cluster.adoc index 2ca39dee6d..2aa5e5eac5 100644 --- a/src/main/antora/modules/ROOT/pages/redis/redis-repositories/cluster.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/redis-repositories/cluster.adoc @@ -13,7 +13,6 @@ The following table shows the details of data on a cluster (based on previous ex |people:e2c7dcee-b8cd-4424-883e-736ce564363e|id for hash|15171|127.0.0.1:7381 |people:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56|id for hash|7373|127.0.0.1:7380 |people:firstname:rand|index|1700|127.0.0.1:7379 -| |=============== ==== @@ -29,7 +28,6 @@ The following table shows what happens when you do (note the change in the slot |\{people}:e2c7dcee-b8cd-4424-883e-736ce564363e|id for hash|2399|127.0.0.1:7379 |\{people}:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56|id for hash|2399|127.0.0.1:7379 |\{people}:firstname:rand|index|2399|127.0.0.1:7379 -| |=============== ==== diff --git a/src/main/antora/modules/ROOT/pages/redis/redis-repositories/expirations.adoc b/src/main/antora/modules/ROOT/pages/redis/redis-repositories/expirations.adoc index 4372f7749b..ddb794213d 100644 --- a/src/main/antora/modules/ROOT/pages/redis/redis-repositories/expirations.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/redis-repositories/expirations.adoc @@ -3,7 +3,7 @@ Objects stored in Redis may be valid only for a certain amount of time. This is especially useful for persisting short-lived objects in Redis without having to remove them manually when they reach their end of life. -The expiration time in seconds can be set with `@RedisHash(timeToLive=...)` as well as by using `KeyspaceSettings` (see xref:redis/redis-repositories/keyspaces.adoc[Keyspaces]). +The expiration time in seconds can be set with `@RedisHash(timeToLive=...)` as well as by using javadoc:org.springframework.data.redis.core.convert.KeyspaceConfiguration$KeyspaceSettings[] (see xref:redis/redis-repositories/keyspaces.adoc[Keyspaces]). More flexible expiration times can be set by using the `@TimeToLive` annotation on either a numeric property or a method. However, do not apply `@TimeToLive` on both a method and a property within the same class. @@ -37,16 +37,16 @@ public class TimeToLiveOnMethod { NOTE: Annotating a property explicitly with `@TimeToLive` reads back the actual `TTL` or `PTTL` value from Redis. -1 indicates that the object has no associated expiration. -The repository implementation ensures subscription to https://redis.io/topics/notifications[Redis keyspace notifications] via `RedisMessageListenerContainer`. +The repository implementation ensures subscription to https://redis.io/topics/notifications[Redis keyspace notifications] via javadoc:org.springframework.data.redis.listener.RedisMessageListenerContainer[]. When the expiration is set to a positive value, the corresponding `EXPIRE` command is run. In addition to persisting the original, a phantom copy is persisted in Redis and set to expire five minutes after the original one. -This is done to enable the Repository support to publish `RedisKeyExpiredEvent`, holding the expired value in Spring's `ApplicationEventPublisher` whenever a key expires, even though the original values have already been removed. +This is done to enable the Repository support to publish javadoc:org.springframework.data.redis.core.RedisKeyExpiredEvent[], holding the expired value in Spring's `ApplicationEventPublisher` whenever a key expires, even though the original values have already been removed. Expiry events are received on all connected applications that use Spring Data Redis repositories. By default, the key expiry listener is disabled when initializing the application. The startup mode can be adjusted in `@EnableRedisRepositories` or `RedisKeyValueAdapter` to start the listener with the application or upon the first insert of an entity with a TTL. -See https://docs.spring.io/spring-data/redis/docs/{version}/api/org/springframework/data/redis/core/RedisKeyValueAdapter.EnableKeyspaceEvents.html[`EnableKeyspaceEvents`] for possible values. +See javadoc:org.springframework.data.redis.core.RedisKeyValueAdapter$EnableKeyspaceEvents[] for possible values. The `RedisKeyExpiredEvent` holds a copy of the expired domain object as well as the key. diff --git a/src/main/antora/modules/ROOT/pages/redis/redis-streams.adoc b/src/main/antora/modules/ROOT/pages/redis/redis-streams.adoc index 97febc2cee..25d916f637 100644 --- a/src/main/antora/modules/ROOT/pages/redis/redis-streams.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/redis-streams.adoc @@ -73,8 +73,8 @@ Due to its blocking nature, low-level polling is not attractive, as it requires Spring Data ships with two implementations tailored to the used programming model: -* `StreamMessageListenerContainer` acts as message listener container for imperative programming models. It is used to consume records from a Redis Stream and drive the `StreamListener` instances that are injected into it. -* `StreamReceiver` provides a reactive variant of a message listener. It is used to consume messages from a Redis Stream as potentially infinite stream and emit stream messages through a `Flux`. +* javadoc:org.springframework.data.redis.stream.StreamMessageListenerContainer[] acts as message listener container for imperative programming models. It is used to consume records from a Redis Stream and drive the javadoc:org.springframework.data.redis.stream.StreamListener[] instances that are injected into it. +* javadoc:org.springframework.data.redis.stream.StreamReceiver[] provides a reactive variant of a message listener. It is used to consume messages from a Redis Stream as potentially infinite stream and emit stream messages through a `Flux`. `StreamMessageListenerContainer` and `StreamReceiver` are responsible for all threading of message reception and dispatch into the listener for processing. A message listener container/receiver is the intermediary between an MDP and a messaging provider and takes care of registering to receive messages, resource acquisition and release, exception conversion, and the like. This lets you as an application developer write the (possibly complex) business logic associated with receiving a message (and reacting to it) and delegates boilerplate Redis infrastructure concerns to the framework. @@ -84,7 +84,7 @@ Both containers allow runtime configuration changes so that you can add or remov [[imperative-streammessagelistenercontainer]] ==== Imperative `StreamMessageListenerContainer` -In a fashion similar to a Message-Driven Bean (MDB) in the EJB world, the Stream-Driven POJO (SDP) acts as a receiver for Stream messages. The one restriction on an SDP is that it must implement the `org.springframework.data.redis.stream.StreamListener` interface. Please also be aware that in the case where your POJO receives messages on multiple threads, it is important to ensure that your implementation is thread-safe. +In a fashion similar to a Message-Driven Bean (MDB) in the EJB world, the Stream-Driven POJO (SDP) acts as a receiver for Stream messages. The one restriction on an SDP is that it must implement the javadoc:org.springframework.data.redis.stream.StreamListener[] interface. Please also be aware that in the case where your POJO receives messages on multiple threads, it is important to ensure that your implementation is thread-safe. [source,java] ---- diff --git a/src/main/antora/modules/ROOT/pages/redis/scripting.adoc b/src/main/antora/modules/ROOT/pages/redis/scripting.adoc index 38864e6e99..436c50d11c 100644 --- a/src/main/antora/modules/ROOT/pages/redis/scripting.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/scripting.adoc @@ -3,9 +3,9 @@ Redis versions 2.6 and higher provide support for running Lua scripts through the https://redis.io/commands/eval[eval] and https://redis.io/commands/evalsha[evalsha] commands. Spring Data Redis provides a high-level abstraction for running scripts that handles serialization and automatically uses the Redis script cache. -Scripts can be run by calling the `execute` methods of `RedisTemplate` and `ReactiveRedisTemplate`. Both use a configurable `ScriptExecutor` (or `ReactiveScriptExecutor`) to run the provided script. By default, the `ScriptExecutor` (or `ReactiveScriptExecutor`) takes care of serializing the provided keys and arguments and deserializing the script result. This is done through the key and value serializers of the template. There is an additional overload that lets you pass custom serializers for the script arguments and the result. +Scripts can be run by calling the `execute` methods of `RedisTemplate` and `ReactiveRedisTemplate`. Both use a configurable javadoc:org.springframework.data.redis.core.script.ScriptExecutor[] (or javadoc:org.springframework.data.redis.core.script.ReactiveScriptExecutor[]) to run the provided script. By default, the javadoc:org.springframework.data.redis.core.script.ScriptExecutor[] (or javadoc:org.springframework.data.redis.core.script.ReactiveScriptExecutor[]) takes care of serializing the provided keys and arguments and deserializing the script result. This is done through the key and value serializers of the template. There is an additional overload that lets you pass custom serializers for the script arguments and the result. -The default `ScriptExecutor` optimizes performance by retrieving the SHA1 of the script and attempting first to run `evalsha`, falling back to `eval` if the script is not yet present in the Redis script cache. +The default javadoc:org.springframework.data.redis.core.script.ScriptExecutor[] optimizes performance by retrieving the SHA1 of the script and attempting first to run `evalsha`, falling back to `eval` if the script is not yet present in the Redis script cache. The following example runs a common "`check-and-set`" scenario by using a Lua script. This is an ideal use case for a Redis script, as it requires that running a set of commands atomically, and the behavior of one command is influenced by the result of another. @@ -69,10 +69,10 @@ end return false ---- -The preceding code configures a `RedisScript` pointing to a file called `checkandset.lua`, which is expected to return a boolean value. The script `resultType` should be one of `Long`, `Boolean`, `List`, or a deserialized value type. It can also be `null` if the script returns a throw-away status (specifically, `OK`). +The preceding code configures a javadoc:org.springframework.data.redis.core.script.RedisScript[] pointing to a file called `checkandset.lua`, which is expected to return a boolean value. The script `resultType` should be one of `Long`, `Boolean`, `List`, or a deserialized value type. It can also be `null` if the script returns a throw-away status (specifically, `OK`). TIP: It is ideal to configure a single instance of `DefaultRedisScript` in your application context to avoid re-calculation of the script's SHA1 on every script run. -The `checkAndSet` method above then runs the scripts. Scripts can be run within a `SessionCallback` as part of a transaction or pipeline. See "`xref:redis/transactions.adoc[Redis Transactions]`" and "`xref:redis/pipelining.adoc[Pipelining]`" for more information. +The `checkAndSet` method above then runs the scripts. Scripts can be run within a javadoc:org.springframework.data.redis.core.SessionCallback[] as part of a transaction or pipeline. See "`xref:redis/transactions.adoc[Redis Transactions]`" and "`xref:redis/pipelining.adoc[Pipelining]`" for more information. The scripting support provided by Spring Data Redis also lets you schedule Redis scripts for periodic running by using the Spring Task and Scheduler abstractions. See the https://spring.io/projects/spring-framework/[Spring Framework] documentation for more details. diff --git a/src/main/antora/modules/ROOT/pages/redis/support-classes.adoc b/src/main/antora/modules/ROOT/pages/redis/support-classes.adoc index 14ae2a6ac9..e363bb4f53 100644 --- a/src/main/antora/modules/ROOT/pages/redis/support-classes.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/support-classes.adoc @@ -4,11 +4,11 @@ Package `org.springframework.data.redis.support` offers various reusable components that rely on Redis as a backing store. Currently, the package contains various JDK-based interface implementations on top of Redis, such as https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/atomic/package-summary.html[atomic] counters and JDK https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Collection.html[Collections]. -NOTE: `RedisList` is forward-compatible with Java 21 `SequencedCollection`. +NOTE: javadoc:org.springframework.data.redis.support.collections.RedisList[] is forward-compatible with Java 21 `SequencedCollection`. The atomic counters make it easy to wrap Redis key incrementation while the collections allow easy management of Redis keys with minimal storage exposure or API leakage. -In particular, the `RedisSet` and `RedisZSet` interfaces offer easy access to the set operations supported by Redis, such as `intersection` and `union`. `RedisList` implements the `List`, `Queue`, and `Deque` contracts (and their equivalent blocking siblings) on top of Redis, exposing the storage as a FIFO (First-In-First-Out), LIFO (Last-In-First-Out) or capped collection with minimal configuration. -The following example shows the configuration for a bean that uses a `RedisList`: +In particular, the javadoc:org.springframework.data.redis.support.collections.RedisSet[] and javadoc:org.springframework.data.redis.support.collections.RedisZSet[] interfaces offer easy access to the set operations supported by Redis, such as `intersection` and `union`. javadoc:org.springframework.data.redis.support.collections.RedisList[] implements the `List`, `Queue`, and `Deque` contracts (and their equivalent blocking siblings) on top of Redis, exposing the storage as a FIFO (First-In-First-Out), LIFO (Last-In-First-Out) or capped collection with minimal configuration. +The following example shows the configuration for a bean that uses a javadoc:org.springframework.data.redis.support.collections.RedisList[]: [tabs] ====== diff --git a/src/main/antora/modules/ROOT/pages/redis/template.adoc b/src/main/antora/modules/ROOT/pages/redis/template.adoc index 38dd0d42e9..a43028834d 100644 --- a/src/main/antora/modules/ROOT/pages/redis/template.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/template.adoc @@ -1,12 +1,12 @@ [[redis:template]] = Working with Objects through `RedisTemplate` -Most users are likely to use `RedisTemplate` and its corresponding package, `org.springframework.data.redis.core` or its reactive variant `ReactiveRedisTemplate`. +Most users are likely to use javadoc:org.springframework.data.redis.core.RedisTemplate[] and its corresponding package, `org.springframework.data.redis.core` or its reactive variant javadoc:org.springframework.data.redis.core.ReactiveRedisTemplate[]. The template is, in fact, the central class of the Redis module, due to its rich feature set. The template offers a high-level abstraction for Redis interactions. While `[Reactive]RedisConnection` offers low-level methods that accept and return binary values (`byte` arrays), the template takes care of serialization and connection management, freeing the user from dealing with such details. -The `RedisTemplate` class implements the `RedisOperations` interface and its reactive variant `ReactiveRedisTemplate` implements `ReactiveRedisOperations`. +The javadoc:org.springframework.data.redis.core.RedisTemplate[] class implements the javadoc:org.springframework.data.redis.core.RedisOperations[] interface and its reactive variant javadoc:org.springframework.data.redis.core.ReactiveRedisTemplate[] implements javadoc:org.springframework.data.redis.core.ReactiveRedisOperations[]. NOTE: The preferred way to reference operations on a `[Reactive]RedisTemplate` instance is through the `[Reactive]RedisOperations` interface. @@ -27,48 +27,48 @@ Imperative:: 2+^|_Key Type Operations_ -|`GeoOperations` +|javadoc:org.springframework.data.redis.core.GeoOperations[] |Redis geospatial operations, such as `GEOADD`, `GEORADIUS`,... -|`HashOperations` +|javadoc:org.springframework.data.redis.core.HashOperations[] |Redis hash operations -|`HyperLogLogOperations` +|javadoc:org.springframework.data.redis.core.HyperLogLogOperations[] |Redis HyperLogLog operations, such as `PFADD`, `PFCOUNT`,... -|`ListOperations` +|javadoc:org.springframework.data.redis.core.ListOperations[] |Redis list operations -|`SetOperations` +|javadoc:org.springframework.data.redis.core.SetOperations[] |Redis set operations -|`ValueOperations` +|javadoc:org.springframework.data.redis.core.ValueOperations[] |Redis string (or value) operations -|`ZSetOperations` +|javadoc:org.springframework.data.redis.core.ZSetOperations[] |Redis zset (or sorted set) operations 2+^|_Key Bound Operations_ -|`BoundGeoOperations` +|javadoc:org.springframework.data.redis.core.BoundGeoOperations[] |Redis key bound geospatial operations -|`BoundHashOperations` +|javadoc:org.springframework.data.redis.core.BoundHashOperations[] |Redis hash key bound operations -|`BoundKeyOperations` +|javadoc:org.springframework.data.redis.core.BoundKeyOperations[] |Redis key bound operations -|`BoundListOperations` +|javadoc:org.springframework.data.redis.core.BoundListOperations[] |Redis list key bound operations -|`BoundSetOperations` +|javadoc:org.springframework.data.redis.core.BoundSetOperations[] |Redis set key bound operations -|`BoundValueOperations` +|javadoc:org.springframework.data.redis.core.BoundValueOperations[] |Redis string (or value) key bound operations -|`BoundZSetOperations` +|javadoc:org.springframework.data.redis.core.BoundZSetOperations[] |Redis zset (or sorted set) key bound operations |==== @@ -82,25 +82,25 @@ Reactive:: 2+^|_Key Type Operations_ -|`ReactiveGeoOperations` +|javadoc:org.springframework.data.redis.core.ReactiveGeoOperations[] |Redis geospatial operations such as `GEOADD`, `GEORADIUS`, and others) -|`ReactiveHashOperations` +|javadoc:org.springframework.data.redis.core.ReactiveHashOperations[] |Redis hash operations -|`ReactiveHyperLogLogOperations` +|javadoc:org.springframework.data.redis.core.ReactiveHyperLogLogOperations[] |Redis HyperLogLog operations such as (`PFADD`, `PFCOUNT`, and others) -|`ReactiveListOperations` +|javadoc:org.springframework.data.redis.core.ReactiveListOperations[] |Redis list operations -|`ReactiveSetOperations` +|javadoc:org.springframework.data.redis.core.ReactiveSetOperations[] |Redis set operations -|`ReactiveValueOperations` +|javadoc:org.springframework.data.redis.core.ReactiveValueOperations[] |Redis string (or value) operations -|`ReactiveZSetOperations` +|javadoc:org.springframework.data.redis.core.ReactiveZSetOperations[] |Redis zset (or sorted set) operations |==== ====== @@ -355,24 +355,24 @@ In Spring Data, the conversion between the user (custom) types and raw data (and This package contains two types of serializers that, as the name implies, take care of the serialization process: -* Two-way serializers based on ``RedisSerializer``. +* Two-way serializers based on javadoc:org.springframework.data.redis.serializer.RedisSerializer[]. * Element readers and writers that use `RedisElementReader` and ``RedisElementWriter``. The main difference between these variants is that `RedisSerializer` primarily serializes to `byte[]` while readers and writers use `ByteBuffer`. Multiple implementations are available (including two that have been already mentioned in this documentation): -* `JdkSerializationRedisSerializer`, which is used by default for `RedisCache` and `RedisTemplate`. +* javadoc:org.springframework.data.redis.serializer.JdkSerializationRedisSerializer[], which is used by default for javadoc:org.springframework.data.redis.cache.RedisCache[] and javadoc:org.springframework.data.redis.core.RedisTemplate[]. * the `StringRedisSerializer`. -However, one can use `OxmSerializer` for Object/XML mapping through Spring {spring-framework-docs}/data-access.html#oxm[OXM] support or `Jackson2JsonRedisSerializer` or `GenericJackson2JsonRedisSerializer` for storing data in https://en.wikipedia.org/wiki/JSON[JSON] format. +However, one can use `OxmSerializer` for Object/XML mapping through Spring {spring-framework-docs}/data-access.html#oxm[OXM] support or javadoc:org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer[] or javadoc:org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer[] for storing data in https://en.wikipedia.org/wiki/JSON[JSON] format. Do note that the storage format is not limited only to values. It can be used for keys, values, or hashes without any restrictions. [WARNING] ==== -By default, `RedisCache` and `RedisTemplate` are configured to use Java native serialization. +By default, javadoc:org.springframework.data.redis.cache.RedisCache[] and javadoc:org.springframework.data.redis.core.RedisTemplate[] are configured to use Java native serialization. Java native serialization is known for allowing the running of remote code caused by payloads that exploit vulnerable libraries and classes injecting unverified bytecode. Manipulated input could lead to unwanted code being run in the application during the deserialization step. As a consequence, do not use serialization in untrusted environments. diff --git a/src/main/antora/modules/ROOT/pages/redis/transactions.adoc b/src/main/antora/modules/ROOT/pages/redis/transactions.adoc index de48a55cc5..8ba20676bc 100644 --- a/src/main/antora/modules/ROOT/pages/redis/transactions.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/transactions.adoc @@ -2,10 +2,10 @@ = Redis Transactions Redis provides support for https://redis.io/topics/transactions[transactions] through the `multi`, `exec`, and `discard` commands. -These operations are available on `RedisTemplate`. +These operations are available on javadoc:org.springframework.data.redis.core.RedisTemplate[]. However, `RedisTemplate` is not guaranteed to run all the operations in the transaction with the same connection. -Spring Data Redis provides the `SessionCallback` interface for use when multiple operations need to be performed with the same `connection`, such as when using Redis transactions.The following example uses the `multi` method: +Spring Data Redis provides the javadoc:org.springframework.data.redis.core.SessionCallback[] interface for use when multiple operations need to be performed with the same `connection`, such as when using Redis transactions.The following example uses the `multi` method: [source,java] ---- From 044b89f55cc87a4e53761eac0eb7a606c6171fae Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 1 Aug 2024 10:19:53 +0200 Subject: [PATCH 018/187] Remove recent version badges from readme. We now no longer have a resource that serves these. Closes #2916 --- README.adoc | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.adoc b/README.adoc index 72089cab87..37cd3dbdf9 100644 --- a/README.adoc +++ b/README.adoc @@ -1,6 +1,3 @@ -image:https://spring.io/badges/spring-data-redis/ga.svg[Spring Data Redis,link=https://spring.io/projects/spring-data-redis/#quick-start] -image:https://spring.io/badges/spring-data-redis/snapshot.svg[Spring Data Redis,link=https://spring.io/projects/spring-data-redis/#quick-start] - = Spring Data Redis image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-redis%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-redis/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=Spring%20Data%20Redis"] The primary goal of the https://spring.io/projects/spring-data/[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services. From 5453b0b0832261ec6c4b9778edbe80771b185d73 Mon Sep 17 00:00:00 2001 From: Eric Haag Date: Thu, 1 Aug 2024 03:22:22 -0500 Subject: [PATCH 019/187] Migrate build to Spring Develocity Conventions extension. * Migrate build to Spring Develocity Conventions extension. * Adopt Develocity environment variables. Closes #2947 --- .gitignore | 2 +- .mvn/extensions.xml | 11 +++-------- .mvn/gradle-enterprise.xml | 31 ------------------------------- Jenkinsfile | 4 ---- ci/pipeline.properties | 1 - ci/test.sh | 5 ----- 6 files changed, 4 insertions(+), 50 deletions(-) delete mode 100644 .mvn/gradle-enterprise.xml diff --git a/.gitignore b/.gitignore index b34bc1769a..a0417bbada 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,4 @@ build/ node_modules node package-lock.json -.mvn/.gradle-enterprise +.mvn/.develocity diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index ebd7610255..1e3bb355f5 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -1,13 +1,8 @@ - com.gradle - gradle-enterprise-maven-extension - 1.19.2 - - - com.gradle - common-custom-user-data-maven-extension - 1.12.4 + io.spring.develocity.conventions + develocity-conventions-maven-extension + 0.0.19 diff --git a/.mvn/gradle-enterprise.xml b/.mvn/gradle-enterprise.xml deleted file mode 100644 index c244063147..0000000000 --- a/.mvn/gradle-enterprise.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - https://ge.spring.io - - - false - true - true - - #{{'0.0.0.0'}} - - - - - true - - - - - ${env.DEVELOCITY_CACHE_USERNAME} - ${env.DEVELOCITY_CACHE_PASSWORD} - - - true - #{env['DEVELOCITY_CACHE_USERNAME'] != null and env['DEVELOCITY_CACHE_PASSWORD'] != null} - - - diff --git a/Jenkinsfile b/Jenkinsfile index d6b8b4fafc..3a9ac6bfff 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -239,7 +239,6 @@ pipeline { environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } @@ -248,9 +247,6 @@ pipeline { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' + - "DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} " + - "DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} " + - "GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} " + "./mvnw -s settings.xml -Pci,artifactory " + "-Dartifactory.server=${p['artifactory.url']} " + "-Dartifactory.username=${ARTIFACTORY_USR} " + diff --git a/ci/pipeline.properties b/ci/pipeline.properties index f024b1feb8..328a975658 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -31,6 +31,5 @@ docker.proxy.credentials=usw1_packages_broadcom_com-jenkins-token artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c artifactory.url=https://repo.spring.io artifactory.repository.snapshot=libs-snapshot-local -develocity.cache.credentials=gradle_enterprise_cache_user develocity.access-key=gradle_enterprise_secret_access_key jenkins.user.name=spring-builds+jenkins diff --git a/ci/test.sh b/ci/test.sh index 06db99ec5d..44267f8b7a 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -10,13 +10,8 @@ cwd=$(pwd) # Launch Redis in proper configuration pushd /tmp && ln -s /work && make -f $cwd/Makefile start && popd -export DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} -export DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} export JENKINS_USER=${JENKINS_USER_NAME} -# The environment variable to configure access key is still GRADLE_ENTERPRISE_ACCESS_KEY -export GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} - # Execute maven test MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean test -P${PROFILE} -DrunLongTests=${LONG_TESTS:-false} -Dredis.server.version=${REDIS_VERSION:-unknown} -U -B From c1521b610e8be0d8fa748fac673a33b15190704f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 1 Aug 2024 10:40:03 +0200 Subject: [PATCH 020/187] Polishing. Remove lingering DEVELOCITY_CACHE credential declarations. Address typo. See #2947 --- Jenkinsfile | 5 ----- README.adoc | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3a9ac6bfff..b10529fb9f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -117,7 +117,6 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES') } environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { @@ -147,7 +146,6 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES') } environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { @@ -167,7 +165,6 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES') } environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { @@ -187,7 +184,6 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES') } environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { @@ -208,7 +204,6 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES') } environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { diff --git a/README.adoc b/README.adoc index 37cd3dbdf9..ea031b6843 100644 --- a/README.adoc +++ b/README.adoc @@ -2,7 +2,7 @@ The primary goal of the https://spring.io/projects/spring-data/[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services. -This modules provides integration with the https://redis.io/[Redis] store. +This module provides integration with the https://redis.io/[Redis] store. == Features From 3e8081926dc85125ef7c5fca10d81fc104d7070b Mon Sep 17 00:00:00 2001 From: Lucian Torje Date: Sat, 3 Aug 2024 00:47:06 +0300 Subject: [PATCH 021/187] Do not attempt to delete phantom key if ShadowCopy is OFF This commit makes sure to consider the ShadowCopy flag for expiring key events and prevents delete attempts if shadow copy is off. Closes: #2954 Original Pull Request: #2955 --- .../data/redis/core/RedisKeyValueAdapter.java | 32 ++++---- .../core/MappingExpirationListenerTest.java | 74 +++++++++++++++++++ 2 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java index bbd96434a0..0be695b4bb 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java @@ -99,6 +99,7 @@ * @author Mark Paluch * @author Andrey Muchnik * @author John Blum + * @author Lucian Torje * @since 1.7 */ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter @@ -735,9 +736,8 @@ private void initMessageListenerContainer() { private void initKeyExpirationListener() { if (this.expirationListener.get() == null) { - MappingExpirationListener listener = new MappingExpirationListener(this.messageListenerContainer, this.redisOps, - this.converter); + this.converter, this.shadowCopy); listener.setKeyspaceNotificationsConfigParameter(keyspaceNotificationsConfigParameter); @@ -763,16 +763,17 @@ static class MappingExpirationListener extends KeyExpirationEventMessageListener private final RedisOperations ops; private final RedisConverter converter; - + private final ShadowCopy shadowCopy; /** * Creates new {@link MappingExpirationListener}. */ MappingExpirationListener(RedisMessageListenerContainer listenerContainer, RedisOperations ops, - RedisConverter converter) { + RedisConverter converter, ShadowCopy shadowCopy) { super(listenerContainer); this.ops = ops; this.converter = converter; + this.shadowCopy = shadowCopy; } @Override @@ -783,22 +784,25 @@ public void onMessage(Message message, @Nullable byte[] pattern) { } byte[] key = message.getBody(); + Object value = null; - byte[] phantomKey = ByteUtils.concat(key, - converter.getConversionService().convert(KeyspaceIdentifier.PHANTOM_SUFFIX, byte[].class)); + if (shadowCopy != ShadowCopy.OFF) { + byte[] phantomKey = ByteUtils.concat(key, + converter.getConversionService().convert(KeyspaceIdentifier.PHANTOM_SUFFIX, byte[].class)); - Map hash = ops.execute((RedisCallback>) connection -> { + Map hash = ops.execute((RedisCallback>) connection -> { - Map phantomValue = connection.hGetAll(phantomKey); + Map phantomValue = connection.hGetAll(phantomKey); - if (!CollectionUtils.isEmpty(phantomValue)) { - connection.del(phantomKey); - } + if (!CollectionUtils.isEmpty(phantomValue)) { + connection.del(phantomKey); + } - return phantomValue; - }); + return phantomValue; + }); - Object value = CollectionUtils.isEmpty(hash) ? null : converter.read(Object.class, new RedisData(hash)); + value = CollectionUtils.isEmpty(hash) ? null : converter.read(Object.class, new RedisData(hash)); + } byte[] channelAsBytes = message.getChannel(); diff --git a/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java b/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java new file mode 100644 index 0000000000..b019fe309e --- /dev/null +++ b/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java @@ -0,0 +1,74 @@ +package org.springframework.data.redis.core; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.core.convert.RedisConverter; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +/** + * @author Lucian Torje + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class MappingExpirationListenerTest { + + @Mock + private RedisOperations redisOperations; + @Mock + private RedisConverter redisConverter; + @Mock + private RedisMessageListenerContainer listenerContainer; + @Mock + private Message message; + @Mock + private RedisKeyExpiredEvent event; + @Mock + private ConversionService conversionService; + + private RedisKeyValueAdapter.MappingExpirationListener listener; + + @Test + void testOnNonKeyExpiration() { + byte[] key = "testKey".getBytes(); + when(message.getBody()).thenReturn(key); + listener = new RedisKeyValueAdapter.MappingExpirationListener(listenerContainer, redisOperations, redisConverter, RedisKeyValueAdapter.ShadowCopy.ON); + + listener.onMessage(message, null); + + verify(redisOperations, times(0)).execute(any(RedisCallback.class)); + } + + @Test + void testOnValidKeyExpiration() { + List eventList = new ArrayList<>(); + + byte[] key = "abc:testKey".getBytes(); + when(message.getBody()).thenReturn(key); + + listener = new RedisKeyValueAdapter.MappingExpirationListener(listenerContainer, redisOperations, redisConverter, RedisKeyValueAdapter.ShadowCopy.OFF); + listener.setApplicationEventPublisher(eventList::add); + listener.onMessage(message, null); + + verify(redisOperations, times(1)).execute(any(RedisCallback.class)); + assertThat(eventList).hasSize(1); + assertThat(eventList.get(0)).isInstanceOf(RedisKeyExpiredEvent.class); + assertThat(((RedisKeyExpiredEvent) (eventList.get(0))).getKeyspace()).isEqualTo("abc"); + assertThat(((RedisKeyExpiredEvent) (eventList.get(0))).getId()).isEqualTo("testKey".getBytes()); + } +} \ No newline at end of file From 57c294e5086b5164f99c76efa2cc094884f144b1 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 6 Aug 2024 09:44:34 +0200 Subject: [PATCH 022/187] Polishing. Move reading shadow copy to dedicated method, update GH issue references, add test and fix formatting. Original Pull Request: #2955 --- .../data/redis/core/RedisKeyValueAdapter.java | 54 ++++--- .../core/MappingExpirationListenerTest.java | 144 +++++++++++------- 2 files changed, 122 insertions(+), 76 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java index 0be695b4bb..926f9d7681 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java @@ -572,8 +572,7 @@ public byte[] createKey(String keyspace, String id) { * Convert given source to binary representation using the underlying {@link ConversionService}. */ public byte[] toBytes(Object source) { - return source instanceof byte[] bytes ? bytes - : getConverter().getConversionService().convert(source, byte[].class); + return source instanceof byte[] bytes ? bytes : getConverter().getConversionService().convert(source, byte[].class); } private String toString(Object value) { @@ -764,6 +763,7 @@ static class MappingExpirationListener extends KeyExpirationEventMessageListener private final RedisOperations ops; private final RedisConverter converter; private final ShadowCopy shadowCopy; + /** * Creates new {@link MappingExpirationListener}. */ @@ -784,26 +784,7 @@ public void onMessage(Message message, @Nullable byte[] pattern) { } byte[] key = message.getBody(); - Object value = null; - - if (shadowCopy != ShadowCopy.OFF) { - byte[] phantomKey = ByteUtils.concat(key, - converter.getConversionService().convert(KeyspaceIdentifier.PHANTOM_SUFFIX, byte[].class)); - - Map hash = ops.execute((RedisCallback>) connection -> { - - Map phantomValue = connection.hGetAll(phantomKey); - - if (!CollectionUtils.isEmpty(phantomValue)) { - connection.del(phantomKey); - } - - return phantomValue; - }); - - value = CollectionUtils.isEmpty(hash) ? null : converter.read(Object.class, new RedisData(hash)); - } - + Object value = readShadowCopyIfEnabled(key); byte[] channelAsBytes = message.getChannel(); String channel = !ObjectUtils.isEmpty(channelAsBytes) @@ -825,6 +806,35 @@ public void onMessage(Message message, @Nullable byte[] pattern) { private boolean isKeyExpirationMessage(Message message) { return BinaryKeyspaceIdentifier.isValid(message.getBody()); } + + @Nullable + private Object readShadowCopyIfEnabled(byte[] key) { + + if (shadowCopy == ShadowCopy.OFF) { + return null; + } + return readShadowCopy(key); + } + + @Nullable + private Object readShadowCopy(byte[] key) { + + byte[] phantomKey = ByteUtils.concat(key, + converter.getConversionService().convert(KeyspaceIdentifier.PHANTOM_SUFFIX, byte[].class)); + + Map hash = ops.execute((RedisCallback>) connection -> { + + Map phantomValue = connection.hGetAll(phantomKey); + + if (!CollectionUtils.isEmpty(phantomValue)) { + connection.del(phantomKey); + } + + return phantomValue; + }); + + return CollectionUtils.isEmpty(hash) ? null : converter.read(Object.class, new RedisData(hash)); + } } private boolean keepShadowCopy() { diff --git a/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java b/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java index b019fe309e..ebb7aa53ab 100644 --- a/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java +++ b/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java @@ -1,74 +1,110 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.data.redis.core; -import org.junit.jupiter.api.BeforeEach; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.convert.ConversionService; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.core.convert.RedisConverter; import org.springframework.data.redis.listener.RedisMessageListenerContainer; -import java.util.ArrayList; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; /** * @author Lucian Torje + * @author Christoph Strobl */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class MappingExpirationListenerTest { - @Mock - private RedisOperations redisOperations; - @Mock - private RedisConverter redisConverter; - @Mock - private RedisMessageListenerContainer listenerContainer; - @Mock - private Message message; - @Mock - private RedisKeyExpiredEvent event; - @Mock - private ConversionService conversionService; - - private RedisKeyValueAdapter.MappingExpirationListener listener; - - @Test - void testOnNonKeyExpiration() { - byte[] key = "testKey".getBytes(); - when(message.getBody()).thenReturn(key); - listener = new RedisKeyValueAdapter.MappingExpirationListener(listenerContainer, redisOperations, redisConverter, RedisKeyValueAdapter.ShadowCopy.ON); - - listener.onMessage(message, null); - - verify(redisOperations, times(0)).execute(any(RedisCallback.class)); - } - - @Test - void testOnValidKeyExpiration() { - List eventList = new ArrayList<>(); - - byte[] key = "abc:testKey".getBytes(); - when(message.getBody()).thenReturn(key); - - listener = new RedisKeyValueAdapter.MappingExpirationListener(listenerContainer, redisOperations, redisConverter, RedisKeyValueAdapter.ShadowCopy.OFF); - listener.setApplicationEventPublisher(eventList::add); - listener.onMessage(message, null); - - verify(redisOperations, times(1)).execute(any(RedisCallback.class)); - assertThat(eventList).hasSize(1); - assertThat(eventList.get(0)).isInstanceOf(RedisKeyExpiredEvent.class); - assertThat(((RedisKeyExpiredEvent) (eventList.get(0))).getKeyspace()).isEqualTo("abc"); - assertThat(((RedisKeyExpiredEvent) (eventList.get(0))).getId()).isEqualTo("testKey".getBytes()); - } -} \ No newline at end of file + @Mock private RedisOperations redisOperations; + @Mock private RedisConverter redisConverter; + @Mock private RedisMessageListenerContainer listenerContainer; + @Mock private Message message; + + private RedisKeyValueAdapter.MappingExpirationListener listener; + + @Test // GH-2954 + void testOnNonKeyExpiration() { + + byte[] key = "testKey".getBytes(); + when(message.getBody()).thenReturn(key); + listener = new RedisKeyValueAdapter.MappingExpirationListener(listenerContainer, redisOperations, redisConverter, + RedisKeyValueAdapter.ShadowCopy.ON); + + listener.onMessage(message, null); + + verify(redisOperations, times(0)).execute(any(RedisCallback.class)); + } + + @Test // GH-2954 + void testOnValidKeyExpirationWithShadowCopiesDisabled() { + + List eventList = new ArrayList<>(); + + byte[] key = "abc:testKey".getBytes(); + when(message.getBody()).thenReturn(key); + + listener = new RedisKeyValueAdapter.MappingExpirationListener(listenerContainer, redisOperations, redisConverter, + RedisKeyValueAdapter.ShadowCopy.OFF); + listener.setApplicationEventPublisher(eventList::add); + listener.onMessage(message, null); + + verify(redisOperations, times(1)).execute(any(RedisCallback.class)); + assertThat(eventList).hasSize(1); + assertThat(eventList.get(0)).isInstanceOf(RedisKeyExpiredEvent.class); + assertThat(((RedisKeyExpiredEvent) (eventList.get(0))).getKeyspace()).isEqualTo("abc"); + assertThat(((RedisKeyExpiredEvent) (eventList.get(0))).getId()).isEqualTo("testKey".getBytes()); + } + + @Test // GH-2954 + void testOnValidKeyExpirationWithShadowCopiesEnabled() { + + ConversionService conversionService = Mockito.mock(ConversionService.class); + List eventList = new ArrayList<>(); + + byte[] key = "abc:testKey".getBytes(); + when(message.getBody()).thenReturn(key); + when(redisConverter.getConversionService()).thenReturn(conversionService); + when(conversionService.convert(any(), eq(byte[].class))).thenReturn("foo".getBytes()); + + listener = new RedisKeyValueAdapter.MappingExpirationListener(listenerContainer, redisOperations, redisConverter, + RedisKeyValueAdapter.ShadowCopy.ON); + listener.setApplicationEventPublisher(eventList::add); + listener.onMessage(message, null); + + verify(redisOperations, times(2)).execute(any(RedisCallback.class)); // delete entry in index, delete phantom key + assertThat(eventList).hasSize(1); + assertThat(eventList.get(0)).isInstanceOf(RedisKeyExpiredEvent.class); + assertThat(((RedisKeyExpiredEvent) (eventList.get(0))).getKeyspace()).isEqualTo("abc"); + assertThat(((RedisKeyExpiredEvent) (eventList.get(0))).getId()).isEqualTo("testKey".getBytes()); + } +} From dd5ca74d91da56bead8d01b070192514f5f3c85c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 8 Aug 2024 10:20:12 +0200 Subject: [PATCH 023/187] Update CI properties. See #2912 --- ci/pipeline.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 328a975658..40bb349196 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,6 +1,6 @@ # Java versions -java.main.tag=17.0.9_9-jdk-focal -java.next.tag=21.0.1_12-jdk-jammy +java.main.tag=17.0.12_7-jdk-focal +java.next.tag=22.0.2_9-jdk-jammy # Docker container images - standard docker.java.main.image=library/eclipse-temurin:${java.main.tag} @@ -14,7 +14,7 @@ docker.mongodb.7.0.version=7.0.2 # Supported versions of Redis docker.redis.6.version=6.2.13 -docker.redis.7.version=7.2.5 +docker.redis.7.version=7.2.4 # Supported versions of Cassandra docker.cassandra.3.version=3.11.16 From dfb65da436f0e833e25ee08ea80ede931f72be74 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 8 Aug 2024 10:22:08 +0200 Subject: [PATCH 024/187] Upgrade to Maven Wrapper 3.9.8. See #2961 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 113cc85066..e0f6d3f781 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Thu Dec 14 08:40:45 CET 2023 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip +#Thu Aug 08 10:22:08 CEST 2024 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip From 5c94b65dd7549474982ce114976c9f6efed57a3d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 8 Aug 2024 14:58:19 +0200 Subject: [PATCH 025/187] CI ping. --- ci/openjdk21-redis-6.2/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/openjdk21-redis-6.2/Dockerfile b/ci/openjdk21-redis-6.2/Dockerfile index d20826b6cb..3505e2ddee 100644 --- a/ci/openjdk21-redis-6.2/Dockerfile +++ b/ci/openjdk21-redis-6.2/Dockerfile @@ -10,7 +10,7 @@ COPY ./Makefile / RUN set -eux; \ # sed -i -e 's/http/https/g' /etc/apt/sources.list ; \ apt-get update ; \ - apt-get install -y build-essential ; \ + apt-get install -y build-essential curl; \ make work/redis/bin/redis-cli work/redis/bin/redis-server VERSION=${VERSION}; \ chmod -R o+rw work; \ apt-get clean; \ From bcb4fd1d74af54703f8c5594c9cc3fe88c74c7b8 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 8 Aug 2024 15:05:27 +0200 Subject: [PATCH 026/187] CI ping. --- ci/openjdk21-redis-6.2/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/openjdk21-redis-6.2/Dockerfile b/ci/openjdk21-redis-6.2/Dockerfile index 3505e2ddee..ea08b24bec 100644 --- a/ci/openjdk21-redis-6.2/Dockerfile +++ b/ci/openjdk21-redis-6.2/Dockerfile @@ -7,6 +7,7 @@ ENV VERSION=${VERSION} # Copy Spring Data Redis's Makefile into the container COPY ./Makefile / + RUN set -eux; \ # sed -i -e 's/http/https/g' /etc/apt/sources.list ; \ apt-get update ; \ From 20688c5a265407f5c6484beeb49c356f5ed703cb Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 7 Aug 2024 10:59:47 +0200 Subject: [PATCH 027/187] Revise RedisKeyValueAdapter to support lifecycle. RedisKeyValueAdapter is now a lifecycle bean participating in Spring's SmartLifecycle support. Sticking to Lifecycle aligns with lifecycle support in RedisConnectionFactory where connections are stopped upon shutdown. Previously, RedisKeyValueAdapter stopped connections upon destroy() causing a delayed shutdown behavior that was out of sync with its RedisConnectionFactory. Closes: #2957 Original Pull Request: #2959 --- .../data/redis/core/RedisKeyValueAdapter.java | 78 ++++++++++++++++--- .../redis/core/RedisKeyValueAdapterTests.java | 8 +- .../core/RedisKeyValueAdapterUnitTests.java | 21 +++++ 3 files changed, 91 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java index 926f9d7681..69338587dd 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java @@ -26,12 +26,16 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationListener; +import org.springframework.context.SmartLifecycle; import org.springframework.core.convert.ConversionService; import org.springframework.data.keyvalue.core.AbstractKeyValueAdapter; import org.springframework.data.keyvalue.core.KeyValueAdapter; @@ -99,17 +103,19 @@ * @author Mark Paluch * @author Andrey Muchnik * @author John Blum - * @author Lucian Torje * @since 1.7 */ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter - implements InitializingBean, ApplicationContextAware, ApplicationListener { + implements InitializingBean, SmartLifecycle, ApplicationContextAware, ApplicationListener { /** * Time To Live in seconds that phantom keys should live longer than the actual key. */ private static final int PHANTOM_KEY_TTL = 300; + private final Log logger = LogFactory.getLog(getClass()); + private final AtomicReference state = new AtomicReference<>(State.CREATED); + private RedisOperations redisOps; private RedisConverter converter; private @Nullable RedisMessageListenerContainer messageListenerContainer; @@ -121,6 +127,13 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter private @Nullable String keyspaceNotificationsConfigParameter = null; private ShadowCopy shadowCopy = ShadowCopy.DEFAULT; + /** + * Lifecycle state of this factory. + */ + enum State { + CREATED, STARTING, STARTED, STOPPING, STOPPED, DESTROYED; + } + /** * Creates new {@link RedisKeyValueAdapter} with default {@link RedisMappingContext} and default * {@link RedisCustomConversions}. @@ -202,7 +215,7 @@ public Object put(Object id, Object item, String keyspace) { && this.expirationListener.get() == null) { if (rdo.getTimeToLive() != null && rdo.getTimeToLive() > 0) { - initKeyExpirationListener(); + initKeyExpirationListener(this.messageListenerContainer); } } @@ -686,6 +699,11 @@ public void setShadowCopy(ShadowCopy shadowCopy) { this.shadowCopy = shadowCopy; } + @Override + public boolean isRunning() { + return State.STARTED.equals(this.state.get()); + } + /** * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() * @since 1.8 @@ -696,22 +714,61 @@ public void afterPropertiesSet() { if (this.managedListenerContainer) { initMessageListenerContainer(); } + } + + @Override + public void start() { + + State current = this.state.getAndUpdate(state -> isCreatedOrStopped(state) ? State.STARTING : state); + + if (isCreatedOrStopped(current)) { + + messageListenerContainer.start(); + + if (ObjectUtils.nullSafeEquals(EnableKeyspaceEvents.ON_STARTUP, this.enableKeyspaceEvents)) { + initKeyExpirationListener(this.messageListenerContainer); + } + + this.state.set(State.STARTED); + } + } + + private static boolean isCreatedOrStopped(@Nullable State state) { + return State.CREATED.equals(state) || State.STOPPED.equals(state); + } + + @Override + public void stop() { - if (ObjectUtils.nullSafeEquals(EnableKeyspaceEvents.ON_STARTUP, this.enableKeyspaceEvents)) { - initKeyExpirationListener(); + if (state.compareAndSet(State.STARTED, State.STOPPING)) { + + KeyExpirationEventMessageListener listener = this.expirationListener.get(); + if (listener != null) { + + if (this.expirationListener.compareAndSet(listener, null)) { + try { + listener.destroy(); + } catch (Exception e) { + logger.warn("Could not destroy KeyExpirationEventMessageListener", e); + } + } + } + + messageListenerContainer.stop(); + state.set(State.STOPPED); } } public void destroy() throws Exception { - if (this.expirationListener.get() != null) { - this.expirationListener.get().destroy(); - } + stop(); if (this.managedListenerContainer && this.messageListenerContainer != null) { this.messageListenerContainer.destroy(); this.messageListenerContainer = null; } + + this.state.set(State.DESTROYED); } @Override @@ -729,13 +786,12 @@ private void initMessageListenerContainer() { this.messageListenerContainer = new RedisMessageListenerContainer(); this.messageListenerContainer.setConnectionFactory(((RedisTemplate) redisOps).getConnectionFactory()); this.messageListenerContainer.afterPropertiesSet(); - this.messageListenerContainer.start(); } - private void initKeyExpirationListener() { + private void initKeyExpirationListener(RedisMessageListenerContainer messageListenerContainer) { if (this.expirationListener.get() == null) { - MappingExpirationListener listener = new MappingExpirationListener(this.messageListenerContainer, this.redisOps, + MappingExpirationListener listener = new MappingExpirationListener(messageListenerContainer, this.redisOps, this.converter, this.shadowCopy); listener.setKeyspaceNotificationsConfigParameter(keyspaceNotificationsConfigParameter); diff --git a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java index 1e23e01c6e..65e57819f3 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java @@ -82,19 +82,17 @@ void setUp() { adapter = new RedisKeyValueAdapter(template, mappingContext); adapter.setEnableKeyspaceEvents(EnableKeyspaceEvents.ON_STARTUP); adapter.afterPropertiesSet(); + adapter.start(); template.execute((RedisCallback) connection -> { connection.flushDb(); return null; }); - RedisConnection connection = template.getConnectionFactory().getConnection(); - - try { + try (RedisConnection connection = template.getConnectionFactory() + .getConnection()) { connection.setConfig("notify-keyspace-events", ""); connection.setConfig("notify-keyspace-events", "KEA"); - } finally { - connection.close(); } } diff --git a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterUnitTests.java b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterUnitTests.java index b992cb5fa4..dca24e7482 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterUnitTests.java @@ -100,6 +100,7 @@ void setUp() throws Exception { adapter = new RedisKeyValueAdapter(template, context); adapter.afterPropertiesSet(); + adapter.start(); } @AfterEach @@ -153,12 +154,32 @@ void shouldInitKeyExpirationListenerOnStartup() throws Exception { adapter = new RedisKeyValueAdapter(template, context); adapter.setEnableKeyspaceEvents(EnableKeyspaceEvents.ON_STARTUP); adapter.afterPropertiesSet(); + adapter.start(); KeyExpirationEventMessageListener listener = ((AtomicReference) getField(adapter, "expirationListener")).get(); assertThat(listener).isNotNull(); } + @Test // GH-2957 + void adapterShouldBeRestartable() throws Exception { + + adapter.destroy(); + + adapter = new RedisKeyValueAdapter(template, context); + adapter.setEnableKeyspaceEvents(EnableKeyspaceEvents.ON_STARTUP); + adapter.afterPropertiesSet(); + adapter.start(); + adapter.stop(); + + assertThat(((AtomicReference) getField(adapter, "expirationListener")).get()) + .isNull(); + + adapter.start(); + assertThat(((AtomicReference) getField(adapter, "expirationListener")).get()) + .isNotNull(); + } + @Test // DATAREDIS-491 void shouldInitKeyExpirationListenerOnFirstPutWithTtl() throws Exception { From 4ec61414c255cdd976256693540c86bfacb391e5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 7 Aug 2024 10:59:57 +0200 Subject: [PATCH 028/187] Polishing. Reorder methods, reformat code. Original Pull Request: #2959 --- .../KeyExpirationEventMessageListener.java | 13 +++-- .../KeyspaceEventMessageListener.java | 57 ++++++++++--------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListener.java b/src/main/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListener.java index f78981fe2c..7a43963ec8 100644 --- a/src/main/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListener.java +++ b/src/main/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListener.java @@ -29,8 +29,8 @@ * @author Christoph Strobl * @since 1.7 */ -public class KeyExpirationEventMessageListener extends KeyspaceEventMessageListener implements - ApplicationEventPublisherAware { +public class KeyExpirationEventMessageListener extends KeyspaceEventMessageListener + implements ApplicationEventPublisherAware { private static final Topic KEYEVENT_EXPIRED_TOPIC = new PatternTopic("__keyevent@*__:expired"); @@ -45,6 +45,11 @@ public KeyExpirationEventMessageListener(RedisMessageListenerContainer listenerC super(listenerContainer); } + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.publisher = applicationEventPublisher; + } + @Override protected void doRegister(RedisMessageListenerContainer listenerContainer) { listenerContainer.addMessageListener(this, KEYEVENT_EXPIRED_TOPIC); @@ -67,8 +72,4 @@ protected void publishEvent(RedisKeyExpiredEvent event) { } } - @Override - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - this.publisher = applicationEventPublisher; - } } diff --git a/src/main/java/org/springframework/data/redis/listener/KeyspaceEventMessageListener.java b/src/main/java/org/springframework/data/redis/listener/KeyspaceEventMessageListener.java index 11e21e49c1..eb61014037 100644 --- a/src/main/java/org/springframework/data/redis/listener/KeyspaceEventMessageListener.java +++ b/src/main/java/org/springframework/data/redis/listener/KeyspaceEventMessageListener.java @@ -22,6 +22,8 @@ import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisServerCommands; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -40,7 +42,7 @@ public abstract class KeyspaceEventMessageListener implements MessageListener, I private final RedisMessageListenerContainer listenerContainer; - private String keyspaceNotificationsConfigParameter = "EA"; + private @Nullable String keyspaceNotificationsConfigParameter = "EA"; /** * Creates new {@link KeyspaceEventMessageListener}. @@ -53,6 +55,26 @@ public KeyspaceEventMessageListener(RedisMessageListenerContainer listenerContai this.listenerContainer = listenerContainer; } + /** + * Set the configuration string to use for {@literal notify-keyspace-events}. + * + * @param keyspaceNotificationsConfigParameter can be {@literal null}. + * @since 1.8 + */ + public void setKeyspaceNotificationsConfigParameter(@Nullable String keyspaceNotificationsConfigParameter) { + this.keyspaceNotificationsConfigParameter = keyspaceNotificationsConfigParameter; + } + + @Override + public void afterPropertiesSet() { + init(); + } + + @Override + public void destroy() throws Exception { + listenerContainer.removeMessageListener(this); + } + @Override public void onMessage(Message message, @Nullable byte[] pattern) { @@ -76,20 +98,18 @@ public void onMessage(Message message, @Nullable byte[] pattern) { */ public void init() { - if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) { + RedisConnectionFactory connectionFactory = listenerContainer.getConnectionFactory(); - RedisConnection connection = listenerContainer.getConnectionFactory().getConnection(); + if (StringUtils.hasText(keyspaceNotificationsConfigParameter) && connectionFactory != null) { - try { + try (RedisConnection connection = connectionFactory.getConnection()) { - Properties config = connection.getConfig("notify-keyspace-events"); + RedisServerCommands commands = connection.serverCommands(); + Properties config = commands.getConfig("notify-keyspace-events"); if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) { - connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter); + commands.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter); } - - } finally { - connection.close(); } } @@ -105,23 +125,4 @@ protected void doRegister(RedisMessageListenerContainer container) { listenerContainer.addMessageListener(this, TOPIC_ALL_KEYEVENTS); } - @Override - public void destroy() throws Exception { - listenerContainer.removeMessageListener(this); - } - - /** - * Set the configuration string to use for {@literal notify-keyspace-events}. - * - * @param keyspaceNotificationsConfigParameter can be {@literal null}. - * @since 1.8 - */ - public void setKeyspaceNotificationsConfigParameter(String keyspaceNotificationsConfigParameter) { - this.keyspaceNotificationsConfigParameter = keyspaceNotificationsConfigParameter; - } - - @Override - public void afterPropertiesSet() throws Exception { - init(); - } } From b41547e8253f2de89d11dee7bd705a2c700106bf Mon Sep 17 00:00:00 2001 From: Zhian Chen Date: Sun, 30 Jun 2024 21:14:59 +1000 Subject: [PATCH 029/187] Replace Lettuce's verifyPeer with verifyMode. Revert modifying type of VerifyMode for DefaultLettuceClientConfiguration constructor Closes #2899 Original pull request: #2934 --- .../DefaultLettuceClientConfiguration.java | 13 ++++++-- ...aultLettucePoolingClientConfiguration.java | 7 ++++ .../lettuce/LettuceClientConfiguration.java | 15 ++++++--- .../lettuce/LettuceConnectionFactory.java | 33 ++++++++++++++++--- .../LettuceClientConfigurationUnitTests.java | 5 +++ .../LettuceConnectionFactoryUnitTests.java | 13 ++++++++ ...cePoolingClientConfigurationUnitTests.java | 4 +++ 7 files changed, 79 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java index c166000bca..976037e1a9 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java @@ -17,6 +17,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.ReadFrom; +import io.lettuce.core.SslVerifyMode; import io.lettuce.core.resource.ClientResources; import java.time.Duration; @@ -30,12 +31,13 @@ * @author Mark Paluch * @author Christoph Strobl * @author Yanming Zhou + * @author Zhian Chen * @since 2.0 */ class DefaultLettuceClientConfiguration implements LettuceClientConfiguration { private final boolean useSsl; - private final boolean verifyPeer; + private final SslVerifyMode verifyMode; private final boolean startTls; private final Optional clientResources; private final Optional clientOptions; @@ -52,7 +54,7 @@ class DefaultLettuceClientConfiguration implements LettuceClientConfiguration { Duration timeout, Duration shutdownTimeout, @Nullable Duration shutdownQuietPeriod) { this.useSsl = useSsl; - this.verifyPeer = verifyPeer; + this.verifyMode = verifyPeer ? SslVerifyMode.FULL : SslVerifyMode.NONE; this.startTls = startTls; this.clientResources = Optional.ofNullable(clientResources); this.clientOptions = Optional.ofNullable(clientOptions); @@ -71,7 +73,12 @@ public boolean isUseSsl() { @Override public boolean isVerifyPeer() { - return verifyPeer; + return verifyMode != SslVerifyMode.NONE; + } + + @Override + public SslVerifyMode getVerifyMode() { + return verifyMode; } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java index fac5f8ec20..70043b16a2 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java @@ -17,6 +17,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.ReadFrom; +import io.lettuce.core.SslVerifyMode; import io.lettuce.core.resource.ClientResources; import java.time.Duration; @@ -30,6 +31,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Yanming Zhou + * @author Zhian Chen * @since 2.0 */ class DefaultLettucePoolingClientConfiguration implements LettucePoolingClientConfiguration { @@ -54,6 +56,11 @@ public boolean isVerifyPeer() { return clientConfiguration.isVerifyPeer(); } + @Override + public SslVerifyMode getVerifyMode() { + return clientConfiguration.getVerifyMode(); + } + @Override public boolean isStartTls() { return clientConfiguration.isStartTls(); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java index 6372226843..77ac8c5d48 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java @@ -18,6 +18,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.ReadFrom; import io.lettuce.core.RedisURI; +import io.lettuce.core.SslVerifyMode; import io.lettuce.core.TimeoutOptions; import io.lettuce.core.resource.ClientResources; @@ -50,6 +51,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Yanming Zhou + * @author Zhian Chen * @since 2.0 * @see org.springframework.data.redis.connection.RedisStandaloneConfiguration * @see org.springframework.data.redis.connection.RedisSentinelConfiguration @@ -67,6 +69,11 @@ public interface LettuceClientConfiguration { */ boolean isVerifyPeer(); + /** + * @return the {@link io.lettuce.core.SslVerifyMode}. + */ + SslVerifyMode getVerifyMode(); + /** * @return {@literal true} to use Start TLS ({@code true} if the first write request shouldn't be encrypted). */ @@ -166,7 +173,7 @@ static LettuceClientConfiguration defaultConfiguration() { class LettuceClientConfigurationBuilder { boolean useSsl; - boolean verifyPeer = true; + SslVerifyMode verifyMode = SslVerifyMode.FULL; boolean startTls; @Nullable ClientResources clientResources; ClientOptions clientOptions = ClientOptions.builder().timeoutOptions(TimeoutOptions.enabled()).build(); @@ -189,7 +196,7 @@ class LettuceClientConfigurationBuilder { public LettuceClientConfigurationBuilder apply(RedisURI redisUri) { this.useSsl = redisUri.isSsl(); - this.verifyPeer = redisUri.isVerifyPeer(); + this.verifyMode = redisUri.getVerifyMode(); this.startTls = redisUri.isStartTls(); if (!redisUri.getTimeout().equals(RedisURI.DEFAULT_TIMEOUT_DURATION)) { @@ -347,7 +354,7 @@ public LettuceClientConfigurationBuilder shutdownQuietPeriod(Duration shutdownQu */ public LettuceClientConfiguration build() { - return new DefaultLettuceClientConfiguration(useSsl, verifyPeer, startTls, clientResources, clientOptions, + return new DefaultLettuceClientConfiguration(useSsl, verifyMode != SslVerifyMode.NONE, startTls, clientResources, clientOptions, clientName, readFrom, redisCredentialsProviderFactory, timeout, shutdownTimeout, shutdownQuietPeriod); } } @@ -372,7 +379,7 @@ class LettuceSslClientConfigurationBuilder { */ public LettuceSslClientConfigurationBuilder disablePeerVerification() { - delegate.verifyPeer = false; + delegate.verifyMode = SslVerifyMode.NONE; return this; } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java index c406faaa1e..f37035f769 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java @@ -24,6 +24,7 @@ import io.lettuce.core.RedisConnectionException; import io.lettuce.core.RedisCredentialsProvider; import io.lettuce.core.RedisURI; +import io.lettuce.core.SslVerifyMode; import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.cluster.ClusterClientOptions; @@ -63,6 +64,7 @@ import org.springframework.data.redis.connection.RedisConfiguration.ClusterConfiguration; import org.springframework.data.redis.connection.RedisConfiguration.WithDatabaseIndex; import org.springframework.data.redis.connection.RedisConfiguration.WithPassword; +import org.springframework.data.redis.connection.lettuce.LettuceConnection.PipeliningFlushPolicy; import org.springframework.data.redis.util.RedisAssertions; import org.springframework.data.util.Optionals; import org.springframework.lang.Nullable; @@ -115,6 +117,7 @@ * @author Andrea Como * @author Chris Bono * @author John Blum + * @author Zhian Chen */ public class LettuceConnectionFactory implements RedisConnectionFactory, ReactiveRedisConnectionFactory, InitializingBean, DisposableBean, SmartLifecycle { @@ -490,6 +493,19 @@ public void setVerifyPeer(boolean verifyPeer) { getMutableConfiguration().setVerifyPeer(verifyPeer); } + /** + * Returns the mode to verify peers when using SSL. + *

+ * FULL will enable a full certificate verification. + * CA means Lettuces only verify the certificate and skip verifying th hostname matches. NONE will disable + * verification and {@link #isVerifyPeer() isVerifyPeer} will return false with this mode. + * + * @return the verify mode of {@link io.lettuce.core.SslVerifyMode}. + */ + public SslVerifyMode getVerifyMode() { + return getMutableConfiguration().getVerifyMode(); + } + /** * Returns whether to issue a StartTLS. * @@ -1360,7 +1376,7 @@ private RedisURI getSentinelRedisURI() { this.clientConfiguration.getClientName().ifPresent(it::setClientName); it.setSsl(this.clientConfiguration.isUseSsl()); - it.setVerifyPeer(this.clientConfiguration.isVerifyPeer()); + it.setVerifyPeer(this.clientConfiguration.getVerifyMode()); it.setStartTls(this.clientConfiguration.isStartTls()); it.setTimeout(this.clientConfiguration.getCommandTimeout()); }); @@ -1659,7 +1675,7 @@ void resetConnection() { static class MutableLettuceClientConfiguration implements LettuceClientConfiguration { private boolean useSsl; - private boolean verifyPeer = true; + private SslVerifyMode verifyMode = SslVerifyMode.FULL; private boolean startTls; private @Nullable ClientResources clientResources; @@ -1680,11 +1696,20 @@ void setUseSsl(boolean useSsl) { @Override public boolean isVerifyPeer() { - return verifyPeer; + return verifyMode != SslVerifyMode.NONE; + } + + @Override + public SslVerifyMode getVerifyMode() { + return verifyMode; } void setVerifyPeer(boolean verifyPeer) { - this.verifyPeer = verifyPeer; + this.verifyMode = verifyPeer? SslVerifyMode.FULL: SslVerifyMode.NONE; + } + + void setVerifyPeer(SslVerifyMode verifyMode) { + this.verifyMode = verifyMode; } @Override diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java index 35c818bb81..261e5ea968 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java @@ -19,6 +19,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.RedisURI; +import io.lettuce.core.SslVerifyMode; import io.lettuce.core.TimeoutOptions; import io.lettuce.core.resource.ClientResources; @@ -34,6 +35,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Yanming Zhou + * @author Zhian Chen */ class LettuceClientConfigurationUnitTests { @@ -45,6 +47,7 @@ void shouldCreateEmptyConfiguration() { assertThat(configuration.isUseSsl()).isFalse(); assertThat(configuration.isVerifyPeer()).isTrue(); + assertThat(configuration.getVerifyMode().equals(SslVerifyMode.FULL)); assertThat(configuration.isStartTls()).isFalse(); assertThat(configuration.getClientOptions()).hasValueSatisfying(actual -> { @@ -78,6 +81,7 @@ void shouldConfigureAllProperties() { assertThat(configuration.isUseSsl()).isTrue(); assertThat(configuration.isVerifyPeer()).isFalse(); + assertThat(configuration.getVerifyMode().equals(SslVerifyMode.NONE)); assertThat(configuration.isStartTls()).isTrue(); assertThat(configuration.getClientOptions()).contains(clientOptions); assertThat(configuration.getClientResources()).contains(sharedClientResources); @@ -115,6 +119,7 @@ void shouldApplySettingsFromRedisURI() { assertThat(configuration.isUseSsl()).isTrue(); assertThat(configuration.isVerifyPeer()).isTrue(); + assertThat(configuration.getVerifyMode().equals(SslVerifyMode.FULL)); assertThat(configuration.isStartTls()).isFalse(); assertThat(configuration.getClientName()).contains("bar"); assertThat(configuration.getCommandTimeout()).isEqualTo(Duration.ofSeconds(10)); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java index b4dd713fbe..952114585f 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java @@ -26,6 +26,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.RedisClient; import io.lettuce.core.RedisURI; +import io.lettuce.core.SslVerifyMode; import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.cluster.ClusterClientOptions; @@ -76,6 +77,7 @@ * @author Andrea Como * @author Chris Bono * @author John Blum + * @author Zhian Chen */ class LettuceConnectionFactoryUnitTests { @@ -374,7 +376,9 @@ void sslOptionsShouldBeDisabledByDefaultOnClient() { assertThat(redisUri.isStartTls()).isFalse(); assertThat(connectionFactory.isStartTls()).isFalse(); assertThat(redisUri.isVerifyPeer()).isTrue(); + assertThat(redisUri.getVerifyMode().equals(SslVerifyMode.FULL)); assertThat(connectionFactory.isVerifyPeer()).isTrue(); + assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.FULL)); } @Test // DATAREDIS-476 @@ -393,7 +397,9 @@ void sslShouldBeSetCorrectlyOnClient() { assertThat(redisUri.isSsl()).isTrue(); assertThat(connectionFactory.isUseSsl()).isTrue(); assertThat(redisUri.isVerifyPeer()).isTrue(); + assertThat(redisUri.getVerifyMode().equals(SslVerifyMode.FULL)); assertThat(connectionFactory.isVerifyPeer()).isTrue(); + assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.FULL)); } @Test // DATAREDIS-480 @@ -411,7 +417,9 @@ void verifyPeerOptionShouldBeSetCorrectlyOnClient() { RedisURI redisUri = (RedisURI) getField(client, "redisURI"); assertThat(redisUri.isVerifyPeer()).isFalse(); + assertThat(redisUri.getVerifyMode().equals(SslVerifyMode.NONE)); assertThat(connectionFactory.isVerifyPeer()).isFalse(); + assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.NONE)); } @Test // DATAREDIS-480 @@ -450,7 +458,9 @@ void sslShouldBeSetCorrectlyOnSentinelClient() { assertThat(redisUri.isSsl()).isTrue(); assertThat(connectionFactory.isUseSsl()).isTrue(); assertThat(redisUri.isVerifyPeer()).isTrue(); + assertThat(redisUri.getVerifyMode().equals(SslVerifyMode.FULL)); assertThat(connectionFactory.isVerifyPeer()).isTrue(); + assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.FULL)); } @Test // DATAREDIS-990 @@ -470,6 +480,7 @@ void verifyPeerOptionShouldBeSetCorrectlyOnSentinelClient() { assertThat(redisUri.isVerifyPeer()).isFalse(); assertThat(connectionFactory.isVerifyPeer()).isFalse(); + assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.NONE)); } @Test // DATAREDIS-990 @@ -545,6 +556,7 @@ void verifyPeerTLSOptionShouldBeSetCorrectlyOnClusterClient() { for (RedisURI uri : initialUris) { assertThat(uri.isVerifyPeer()).isTrue(); + assertThat(uri.getVerifyMode().equals(SslVerifyMode.FULL)); } } @@ -745,6 +757,7 @@ void shouldApplyClientConfiguration() { assertThat(connectionFactory.isUseSsl()).isTrue(); assertThat(connectionFactory.isVerifyPeer()).isFalse(); + assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.NONE)); assertThat(connectionFactory.isStartTls()).isTrue(); assertThat(connectionFactory.getClientResources()).isEqualTo(sharedClientResources); assertThat(connectionFactory.getTimeout()).isEqualTo(Duration.ofMinutes(5).toMillis()); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java index 85215098ff..0cae2e9ed4 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java @@ -19,6 +19,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.ReadFrom; +import io.lettuce.core.SslVerifyMode; import io.lettuce.core.TimeoutOptions; import io.lettuce.core.resource.ClientResources; @@ -35,6 +36,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Longlong Zhao + * @author Zhian Chen */ class LettucePoolingClientConfigurationUnitTests { @@ -46,6 +48,7 @@ void shouldCreateEmptyConfiguration() { assertThat(configuration.getPoolConfig()).isNotNull(); assertThat(configuration.isUseSsl()).isFalse(); assertThat(configuration.isVerifyPeer()).isTrue(); + assertThat(configuration.getVerifyMode().equals(SslVerifyMode.FULL)); assertThat(configuration.isStartTls()).isFalse(); assertThat(configuration.getClientOptions()).hasValueSatisfying(actual -> { @@ -80,6 +83,7 @@ void shouldConfigureAllProperties() { assertThat(configuration.getPoolConfig()).isEqualTo(poolConfig); assertThat(configuration.isUseSsl()).isTrue(); assertThat(configuration.isVerifyPeer()).isFalse(); + assertThat(configuration.getVerifyMode().equals(SslVerifyMode.NONE)); assertThat(configuration.isStartTls()).isTrue(); assertThat(configuration.getClientOptions()).contains(clientOptions); assertThat(configuration.getClientResources()).contains(sharedClientResources); From ba2c9bae9001745369fc12a87730d49ecd60f693 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 8 Aug 2024 15:29:42 +0200 Subject: [PATCH 030/187] Polishing. Remove verifyMode setters on LettuceConnectionFactory to not expose additional properties already exposed via ClientConfiguration. Deprecate LettuceClientConfiguration.isVerifyPeer in favor of getVerifyMode. See #2899 Original pull request: #2934 --- .../DefaultLettuceClientConfiguration.java | 4 +-- ...aultLettucePoolingClientConfiguration.java | 1 + .../lettuce/LettuceClientConfiguration.java | 25 +++++++++++++++---- .../lettuce/LettuceConnectionFactory.java | 24 +++--------------- .../LettuceConnectionFactoryUnitTests.java | 12 ++++----- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java index 976037e1a9..c771163f9f 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java @@ -48,13 +48,13 @@ class DefaultLettuceClientConfiguration implements LettuceClientConfiguration { private final Duration shutdownTimeout; private final Duration shutdownQuietPeriod; - DefaultLettuceClientConfiguration(boolean useSsl, boolean verifyPeer, boolean startTls, + DefaultLettuceClientConfiguration(boolean useSsl, SslVerifyMode verifyMode, boolean startTls, @Nullable ClientResources clientResources, @Nullable ClientOptions clientOptions, @Nullable String clientName, @Nullable ReadFrom readFrom, @Nullable RedisCredentialsProviderFactory redisCredentialsProviderFactory, Duration timeout, Duration shutdownTimeout, @Nullable Duration shutdownQuietPeriod) { this.useSsl = useSsl; - this.verifyMode = verifyPeer ? SslVerifyMode.FULL : SslVerifyMode.NONE; + this.verifyMode = verifyMode; this.startTls = startTls; this.clientResources = Optional.ofNullable(clientResources); this.clientOptions = Optional.ofNullable(clientOptions); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java index 70043b16a2..6a89a2bd3d 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java @@ -52,6 +52,7 @@ public boolean isUseSsl() { } @Override + @Deprecated public boolean isVerifyPeer() { return clientConfiguration.isVerifyPeer(); } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java index 77ac8c5d48..eea4e340c6 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java @@ -66,11 +66,14 @@ public interface LettuceClientConfiguration { /** * @return {@literal true} to verify peers when using {@link #isUseSsl() SSL}. + * @deprecated since 3.4, use {@link #getVerifyMode()} for how peer verification is configured. */ + @Deprecated(since = "3.4") boolean isVerifyPeer(); /** * @return the {@link io.lettuce.core.SslVerifyMode}. + * @since 3.4 */ SslVerifyMode getVerifyMode(); @@ -354,7 +357,7 @@ public LettuceClientConfigurationBuilder shutdownQuietPeriod(Duration shutdownQu */ public LettuceClientConfiguration build() { - return new DefaultLettuceClientConfiguration(useSsl, verifyMode != SslVerifyMode.NONE, startTls, clientResources, clientOptions, + return new DefaultLettuceClientConfiguration(useSsl, verifyMode, startTls, clientResources, clientOptions, clientName, readFrom, redisCredentialsProviderFactory, timeout, shutdownTimeout, shutdownQuietPeriod); } } @@ -364,7 +367,7 @@ public LettuceClientConfiguration build() { */ class LettuceSslClientConfigurationBuilder { - private LettuceClientConfigurationBuilder delegate; + private final LettuceClientConfigurationBuilder delegate; LettuceSslClientConfigurationBuilder(LettuceClientConfigurationBuilder delegate) { @@ -373,16 +376,28 @@ class LettuceSslClientConfigurationBuilder { } /** - * Disable peer verification. + * Configure peer verification. * * @return {@literal this} builder. + * @since 3.4 */ - public LettuceSslClientConfigurationBuilder disablePeerVerification() { + public LettuceSslClientConfigurationBuilder verifyPeer(SslVerifyMode verifyMode) { + + Assert.notNull(verifyMode, "SslVerifyMode must not be null"); - delegate.verifyMode = SslVerifyMode.NONE; + delegate.verifyMode = verifyMode; return this; } + /** + * Disable peer verification. + * + * @return {@literal this} builder. + */ + public LettuceSslClientConfigurationBuilder disablePeerVerification() { + return verifyPeer(SslVerifyMode.NONE); + } + /** * Enable Start TLS to send the first bytes unencrypted. * diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java index f37035f769..e673c8257d 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java @@ -64,7 +64,6 @@ import org.springframework.data.redis.connection.RedisConfiguration.ClusterConfiguration; import org.springframework.data.redis.connection.RedisConfiguration.WithDatabaseIndex; import org.springframework.data.redis.connection.RedisConfiguration.WithPassword; -import org.springframework.data.redis.connection.lettuce.LettuceConnection.PipeliningFlushPolicy; import org.springframework.data.redis.util.RedisAssertions; import org.springframework.data.util.Optionals; import org.springframework.lang.Nullable; @@ -476,7 +475,9 @@ public void setUseSsl(boolean useSsl) { * Returns whether to verify certificate validity/hostname check when SSL is used. * * @return whether to verify peers when using SSL. + * @deprecated since 3.4, use {@link LettuceClientConfiguration#getVerifyMode()} instead. */ + @Deprecated(since = "3.4") public boolean isVerifyPeer() { return clientConfiguration.isVerifyPeer(); } @@ -493,19 +494,6 @@ public void setVerifyPeer(boolean verifyPeer) { getMutableConfiguration().setVerifyPeer(verifyPeer); } - /** - * Returns the mode to verify peers when using SSL. - *

- * FULL will enable a full certificate verification. - * CA means Lettuces only verify the certificate and skip verifying th hostname matches. NONE will disable - * verification and {@link #isVerifyPeer() isVerifyPeer} will return false with this mode. - * - * @return the verify mode of {@link io.lettuce.core.SslVerifyMode}. - */ - public SslVerifyMode getVerifyMode() { - return getMutableConfiguration().getVerifyMode(); - } - /** * Returns whether to issue a StartTLS. * @@ -1479,7 +1467,7 @@ private RedisURI createRedisURIAndApplySettings(String host, int port) { builder.withDatabase(getDatabase()); builder.withSsl(clientConfiguration.isUseSsl()); - builder.withVerifyPeer(clientConfiguration.isVerifyPeer()); + builder.withVerifyPeer(clientConfiguration.getVerifyMode()); builder.withStartTls(clientConfiguration.isStartTls()); builder.withTimeout(clientConfiguration.getCommandTimeout()); @@ -1705,11 +1693,7 @@ public SslVerifyMode getVerifyMode() { } void setVerifyPeer(boolean verifyPeer) { - this.verifyMode = verifyPeer? SslVerifyMode.FULL: SslVerifyMode.NONE; - } - - void setVerifyPeer(SslVerifyMode verifyMode) { - this.verifyMode = verifyMode; + this.verifyMode = verifyPeer ? SslVerifyMode.FULL : SslVerifyMode.NONE; } @Override diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java index 952114585f..548fce0f49 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java @@ -378,7 +378,7 @@ void sslOptionsShouldBeDisabledByDefaultOnClient() { assertThat(redisUri.isVerifyPeer()).isTrue(); assertThat(redisUri.getVerifyMode().equals(SslVerifyMode.FULL)); assertThat(connectionFactory.isVerifyPeer()).isTrue(); - assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.FULL)); + assertThat(connectionFactory.getClientConfiguration().getVerifyMode().equals(SslVerifyMode.FULL)); } @Test // DATAREDIS-476 @@ -399,7 +399,7 @@ void sslShouldBeSetCorrectlyOnClient() { assertThat(redisUri.isVerifyPeer()).isTrue(); assertThat(redisUri.getVerifyMode().equals(SslVerifyMode.FULL)); assertThat(connectionFactory.isVerifyPeer()).isTrue(); - assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.FULL)); + assertThat(connectionFactory.getClientConfiguration().getVerifyMode().equals(SslVerifyMode.FULL)); } @Test // DATAREDIS-480 @@ -419,7 +419,7 @@ void verifyPeerOptionShouldBeSetCorrectlyOnClient() { assertThat(redisUri.isVerifyPeer()).isFalse(); assertThat(redisUri.getVerifyMode().equals(SslVerifyMode.NONE)); assertThat(connectionFactory.isVerifyPeer()).isFalse(); - assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.NONE)); + assertThat(connectionFactory.getClientConfiguration().getVerifyMode().equals(SslVerifyMode.NONE)); } @Test // DATAREDIS-480 @@ -460,7 +460,7 @@ void sslShouldBeSetCorrectlyOnSentinelClient() { assertThat(redisUri.isVerifyPeer()).isTrue(); assertThat(redisUri.getVerifyMode().equals(SslVerifyMode.FULL)); assertThat(connectionFactory.isVerifyPeer()).isTrue(); - assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.FULL)); + assertThat(connectionFactory.getClientConfiguration().getVerifyMode().equals(SslVerifyMode.FULL)); } @Test // DATAREDIS-990 @@ -480,7 +480,7 @@ void verifyPeerOptionShouldBeSetCorrectlyOnSentinelClient() { assertThat(redisUri.isVerifyPeer()).isFalse(); assertThat(connectionFactory.isVerifyPeer()).isFalse(); - assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.NONE)); + assertThat(connectionFactory.getClientConfiguration().getVerifyMode().equals(SslVerifyMode.NONE)); } @Test // DATAREDIS-990 @@ -757,7 +757,7 @@ void shouldApplyClientConfiguration() { assertThat(connectionFactory.isUseSsl()).isTrue(); assertThat(connectionFactory.isVerifyPeer()).isFalse(); - assertThat(connectionFactory.getVerifyMode().equals(SslVerifyMode.NONE)); + assertThat(connectionFactory.getClientConfiguration().getVerifyMode().equals(SslVerifyMode.NONE)); assertThat(connectionFactory.isStartTls()).isTrue(); assertThat(connectionFactory.getClientResources()).isEqualTo(sharedClientResources); assertThat(connectionFactory.getTimeout()).isEqualTo(Duration.ofMinutes(5).toMillis()); From 369b549b3c97a056f47fab16e3011dee9e2f74fc Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 8 Aug 2024 15:48:48 +0200 Subject: [PATCH 031/187] Default Lettuce shutdownQuietPeriod to zero. Use a lower timeout for faster shutdown. Also, the quiet period is no longer configured through the shutdown timeout. Closes #2945 --- .../lettuce/DefaultLettuceClientConfiguration.java | 4 ++-- .../connection/lettuce/LettuceClientConfiguration.java | 2 +- .../lettuce/LettuceClientConfigurationUnitTests.java | 9 ++++----- .../LettucePoolingClientConfigurationUnitTests.java | 4 ++-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java index c771163f9f..05035f5fae 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java @@ -51,7 +51,7 @@ class DefaultLettuceClientConfiguration implements LettuceClientConfiguration { DefaultLettuceClientConfiguration(boolean useSsl, SslVerifyMode verifyMode, boolean startTls, @Nullable ClientResources clientResources, @Nullable ClientOptions clientOptions, @Nullable String clientName, @Nullable ReadFrom readFrom, @Nullable RedisCredentialsProviderFactory redisCredentialsProviderFactory, - Duration timeout, Duration shutdownTimeout, @Nullable Duration shutdownQuietPeriod) { + Duration timeout, Duration shutdownTimeout, Duration shutdownQuietPeriod) { this.useSsl = useSsl; this.verifyMode = verifyMode; @@ -63,7 +63,7 @@ class DefaultLettuceClientConfiguration implements LettuceClientConfiguration { this.redisCredentialsProviderFactory = Optional.ofNullable(redisCredentialsProviderFactory); this.timeout = timeout; this.shutdownTimeout = shutdownTimeout; - this.shutdownQuietPeriod = shutdownQuietPeriod != null ? shutdownQuietPeriod : shutdownTimeout; + this.shutdownQuietPeriod = shutdownQuietPeriod; } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java index eea4e340c6..91e243ffaa 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java @@ -185,7 +185,7 @@ class LettuceClientConfigurationBuilder { @Nullable RedisCredentialsProviderFactory redisCredentialsProviderFactory; Duration timeout = Duration.ofSeconds(RedisURI.DEFAULT_TIMEOUT); Duration shutdownTimeout = Duration.ofMillis(100); - @Nullable Duration shutdownQuietPeriod; + Duration shutdownQuietPeriod = Duration.ZERO; LettuceClientConfigurationBuilder() {} diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java index 261e5ea968..288aa48151 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java @@ -39,12 +39,11 @@ */ class LettuceClientConfigurationUnitTests { - @Test // DATAREDIS-574, DATAREDIS-576, DATAREDIS-667, DATAREDIS-918 + @Test // DATAREDIS-574, DATAREDIS-576, DATAREDIS-667, DATAREDIS-918, GH-2945 void shouldCreateEmptyConfiguration() { LettuceClientConfiguration configuration = LettuceClientConfiguration.defaultConfiguration(); - assertThat(configuration.isUseSsl()).isFalse(); assertThat(configuration.isVerifyPeer()).isTrue(); assertThat(configuration.getVerifyMode().equals(SslVerifyMode.FULL)); @@ -58,7 +57,7 @@ void shouldCreateEmptyConfiguration() { assertThat(configuration.getClientName()).isEmpty(); assertThat(configuration.getCommandTimeout()).isEqualTo(Duration.ofSeconds(60)); assertThat(configuration.getShutdownTimeout()).isEqualTo(Duration.ofMillis(100)); - assertThat(configuration.getShutdownQuietPeriod()).isEqualTo(Duration.ofMillis(100)); + assertThat(configuration.getShutdownQuietPeriod()).isEqualTo(Duration.ZERO); } @Test // DATAREDIS-574, DATAREDIS-576, DATAREDIS-667 @@ -92,13 +91,13 @@ void shouldConfigureAllProperties() { } @Test // DATAREDIS-881 - void shutdownQuietPeriodShouldDefaultToTimeout() { + void shutdownQuietPeriodShouldDefaultInitialValue() { LettuceClientConfiguration configuration = LettuceClientConfiguration.builder() .shutdownTimeout(Duration.ofSeconds(42)).build(); assertThat(configuration.getShutdownTimeout()).isEqualTo(Duration.ofSeconds(42)); - assertThat(configuration.getShutdownQuietPeriod()).isEqualTo(Duration.ofSeconds(42)); + assertThat(configuration.getShutdownQuietPeriod()).isEqualTo(Duration.ZERO); } @Test // DATAREDIS-576 diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java index 0cae2e9ed4..ba9fd4a31a 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java @@ -40,7 +40,7 @@ */ class LettucePoolingClientConfigurationUnitTests { - @Test // DATAREDIS-667, DATAREDIS-918 + @Test // DATAREDIS-667, DATAREDIS-918, GH-2945 void shouldCreateEmptyConfiguration() { LettucePoolingClientConfiguration configuration = LettucePoolingClientConfiguration.defaultConfiguration(); @@ -58,7 +58,7 @@ void shouldCreateEmptyConfiguration() { assertThat(configuration.getClientResources()).isEmpty(); assertThat(configuration.getCommandTimeout()).isEqualTo(Duration.ofSeconds(60)); assertThat(configuration.getShutdownTimeout()).isEqualTo(Duration.ofMillis(100)); - assertThat(configuration.getShutdownQuietPeriod()).isEqualTo(Duration.ofMillis(100)); + assertThat(configuration.getShutdownQuietPeriod()).isEqualTo(Duration.ZERO); } @Test // DATAREDIS-667 From af70648cb7185cf79aca5747fb6a9bbdf7b9d769 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 31 Jul 2024 15:09:50 +0200 Subject: [PATCH 032/187] Add support for using custom BeanNameGenerator. Closes: #2951 --- .../EnableRedisRepositories.java | 8 ++ .../RedisRepositoriesRegistrarUnitTests.java | 95 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrarUnitTests.java diff --git a/src/main/java/org/springframework/data/redis/repository/configuration/EnableRedisRepositories.java b/src/main/java/org/springframework/data/redis/repository/configuration/EnableRedisRepositories.java index eea89db92b..54058c38ea 100644 --- a/src/main/java/org/springframework/data/redis/repository/configuration/EnableRedisRepositories.java +++ b/src/main/java/org/springframework/data/redis/repository/configuration/EnableRedisRepositories.java @@ -23,6 +23,7 @@ import java.lang.annotation.Target; import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Import; import org.springframework.data.keyvalue.core.KeyValueOperations; @@ -126,6 +127,13 @@ */ Class repositoryBaseClass() default DefaultRepositoryBaseClass.class; + /** + * Configure a specific {@link BeanNameGenerator} to be used when creating the repositoy beans. + * @return the {@link BeanNameGenerator} to be used or the base {@link BeanNameGenerator} interface to indicate context default. + * @since 3.4 + */ + Class nameGenerator() default BeanNameGenerator.class; + /** * Configures the name of the {@link KeyValueOperations} bean to be used with the repositories detected. * diff --git a/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrarUnitTests.java b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrarUnitTests.java new file mode 100644 index 0000000000..7e3d27fbf3 --- /dev/null +++ b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrarUnitTests.java @@ -0,0 +1,95 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.repository.configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.annotation.AnnotationBeanNameGenerator; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.repository.CrudRepository; + +/** + * @author Christoph Strobl + */ +class RedisRepositoriesRegistrarUnitTests { + + private BeanDefinitionRegistry registry; + + @BeforeEach + void setUp() { + registry = new DefaultListableBeanFactory(); + } + + @ParameterizedTest // GH-499, GH-3440 + @MethodSource(value = { "args" }) + void configuresRepositoriesCorrectly(AnnotationMetadata metadata, String[] beanNames) { + + RedisRepositoriesRegistrar registrar = new RedisRepositoriesRegistrar(); + registrar.setResourceLoader(new DefaultResourceLoader()); + registrar.setEnvironment(new StandardEnvironment()); + registrar.registerBeanDefinitions(metadata, registry); + + Iterable names = Arrays.asList(registry.getBeanDefinitionNames()); + assertThat(names).contains(beanNames); + } + + static Stream args() { + return Stream.of( + Arguments.of(AnnotationMetadata.introspect(Config.class), + new String[] { "redisRepositoriesRegistrarUnitTests.PersonRepository" }), + Arguments.of(AnnotationMetadata.introspect(ConfigWithBeanNameGenerator.class), + new String[] { "redisRepositoriesRegistrarUnitTests.PersonREPO" })); + } + + @EnableRedisRepositories(basePackageClasses = PersonRepository.class, considerNestedRepositories = true) + private class Config { + + } + + @EnableRedisRepositories(basePackageClasses = PersonRepository.class, nameGenerator = MyBeanNameGenerator.class, + considerNestedRepositories = true) + private class ConfigWithBeanNameGenerator { + + } + + static class MyBeanNameGenerator extends AnnotationBeanNameGenerator { + + @Override + public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { + return super.generateBeanName(definition, registry).replaceAll("Repository", "REPO"); + } + } + + interface PersonRepository extends CrudRepository { + + } + + @RedisHash + static class Person {} +} From 67fc8c4da4cb3284f24e75aa7b14eacca7f73fa0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 12 Aug 2024 08:35:34 +0200 Subject: [PATCH 033/187] Upgrade to Jedis 5.1.4. Closes #2964 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f39b1fe04c..c1dd0d47be 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 1.4.20 2.11.1 6.3.2.RELEASE - 5.0.2 + 5.1.4 1.01 4.1.107.Final spring.data.redis From aab1f9b6b2e37c5aa82139912c30dacbc768b722 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 12 Aug 2024 08:35:47 +0200 Subject: [PATCH 034/187] Upgrade to Lettuce 6.4. Closes #2963 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1dd0d47be..792fe40817 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 1.9.4 1.4.20 2.11.1 - 6.3.2.RELEASE + 6.4.0.RELEASE 5.1.4 1.01 4.1.107.Final From 955b8918a45a2b134f47220c992b7c3c820ef627 Mon Sep 17 00:00:00 2001 From: arefbehboudi Date: Sun, 18 Aug 2024 10:45:30 +0330 Subject: [PATCH 035/187] Consistently use instanceof pattern variable. Closes #2967 Original pull request: #2971 --- .../AbstractConnectionUnitTestBase.java | 3 +- .../LettuceReactiveClusterTestSupport.java | 8 +++--- .../LettuceReactiveCommandsTestSupport.java | 28 +++++++++---------- .../DefaultRedisTypeMapperUnitTests.java | 2 +- .../data/redis/listener/PubSubAwaitUtil.java | 8 +++--- .../listener/PubSubResubscribeTests.java | 8 +++--- .../data/redis/listener/PubSubTests.java | 8 +++--- .../Jackson2HashMapperIntegrationTests.java | 4 +-- .../data/redis/repository/cdi/Person.java | 4 +-- .../cdi/RedisCdiDependenciesProducer.java | 4 +-- ...cJackson2JsonRedisSerializerUnitTests.java | 3 +- ...sageListenerContainerIntegrationTests.java | 4 +-- .../BoundKeyOperationsIntegrationTests.java | 16 +++++------ .../test/util/CollectionAwareComparator.java | 5 +--- 14 files changed, 49 insertions(+), 56 deletions(-) diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionUnitTestBase.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionUnitTestBase.java index 050ffaee56..820f0c4c0d 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionUnitTestBase.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionUnitTestBase.java @@ -55,8 +55,7 @@ private Class resolveReturnedClassFromGernericType() { private ParameterizedType resolveReturnedClassFromGernericType(Class clazz) { Object genericSuperclass = clazz.getGenericSuperclass(); - if (genericSuperclass instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; + if (genericSuperclass instanceof ParameterizedType parameterizedType) { Type rawtype = parameterizedType.getRawType(); if (AbstractConnectionUnitTestBase.class.equals(rawtype)) { return parameterizedType; diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterTestSupport.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterTestSupport.java index d253e49cf9..ec079c5ec6 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterTestSupport.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterTestSupport.java @@ -52,12 +52,12 @@ public void tearDown() { if (nativeCommands != null) { nativeCommands.flushall(); - if (nativeCommands instanceof RedisCommands) { - ((RedisCommands) nativeCommands).getStatefulConnection().close(); + if (nativeCommands instanceof RedisCommands redisCommands) { + redisCommands.getStatefulConnection().close(); } - if (nativeCommands instanceof RedisAdvancedClusterCommands) { - ((RedisAdvancedClusterCommands) nativeCommands).getStatefulConnection().close(); + if (nativeCommands instanceof RedisAdvancedClusterCommands redisAdvancedClusterCommands) { + redisAdvancedClusterCommands.getStatefulConnection().close(); } } diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestSupport.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestSupport.java index d06df9f15b..d0a74f4586 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestSupport.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestSupport.java @@ -155,16 +155,16 @@ public String toString() { public void close() throws IOException { try { - if (connectionProvider instanceof DisposableBean) { - ((DisposableBean) connectionProvider).destroy(); + if (connectionProvider instanceof DisposableBean disposableBean) { + disposableBean.destroy(); } - if (nativeConnectionProvider instanceof DisposableBean) { - ((DisposableBean) nativeConnectionProvider).destroy(); + if (nativeConnectionProvider instanceof DisposableBean disposableBean) { + disposableBean.destroy(); } - if (nativeBinaryConnectionProvider instanceof DisposableBean) { - ((DisposableBean) nativeBinaryConnectionProvider).destroy(); + if (nativeBinaryConnectionProvider instanceof DisposableBean disposableBean) { + disposableBean.destroy(); } } catch (Exception ex) { throw new RuntimeException(ex); @@ -198,21 +198,21 @@ public void tearDown() { if (nativeCommands != null) { flushAll(); - if (nativeCommands instanceof RedisCommands) { - nativeConnectionProvider.release(((RedisCommands) nativeCommands).getStatefulConnection()); + if (nativeCommands instanceof RedisCommands redisCommands) { + nativeConnectionProvider.release((redisCommands).getStatefulConnection()); } - if (nativeCommands instanceof RedisAdvancedClusterCommands) { - nativeConnectionProvider.release(((RedisAdvancedClusterCommands) nativeCommands).getStatefulConnection()); + if (nativeCommands instanceof RedisAdvancedClusterCommands redisAdvancedClusterCommands) { + nativeConnectionProvider.release((redisAdvancedClusterCommands).getStatefulConnection()); } - if (nativeBinaryCommands instanceof RedisCommands) { - nativeBinaryConnectionProvider.release(((RedisCommands) nativeBinaryCommands).getStatefulConnection()); + if (nativeBinaryCommands instanceof RedisCommands redisCommands) { + nativeBinaryConnectionProvider.release((redisCommands).getStatefulConnection()); } - if (nativeBinaryCommands instanceof RedisAdvancedClusterCommands) { + if (nativeBinaryCommands instanceof RedisAdvancedClusterCommands redisAdvancedClusterCommands) { nativeBinaryConnectionProvider - .release(((RedisAdvancedClusterCommands) nativeBinaryCommands).getStatefulConnection()); + .release((redisAdvancedClusterCommands).getStatefulConnection()); } } diff --git a/src/test/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapperUnitTests.java b/src/test/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapperUnitTests.java index fa5943e91c..b1c6b6d2cf 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapperUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapperUnitTests.java @@ -215,7 +215,7 @@ private void writesTypeToField(Bucket bucket, Class type, @Nullable Object va assertThat(bucket.keySet()).isEmpty(); } else { - byte[] expected = value instanceof Class ? ((Class) value).getName().getBytes() : value.toString().getBytes(); + byte[] expected = value instanceof Class javaClass ? javaClass.getName().getBytes() : value.toString().getBytes(); assertThat(bucket.asMap()).containsKey(DefaultRedisTypeMapper.DEFAULT_TYPE_KEY); assertThat(bucket.get(DefaultRedisTypeMapper.DEFAULT_TYPE_KEY)).isEqualTo(expected); diff --git a/src/test/java/org/springframework/data/redis/listener/PubSubAwaitUtil.java b/src/test/java/org/springframework/data/redis/listener/PubSubAwaitUtil.java index 4b79d20d6a..66ec4a4207 100644 --- a/src/test/java/org/springframework/data/redis/listener/PubSubAwaitUtil.java +++ b/src/test/java/org/springframework/data/redis/listener/PubSubAwaitUtil.java @@ -87,8 +87,8 @@ public static void runAndAwaitChannelSubscription(RedisConnectionFactory connect private static long numPat(RedisConnection connection) { - if (connection instanceof LettuceConnection) { - return ((Number) ((LettuceConnection) connection).execute("PUBSUB", new IntegerOutput<>(ByteArrayCodec.INSTANCE), + if (connection instanceof LettuceConnection lettuceConnection) { + return ((Number) lettuceConnection.execute("PUBSUB", new IntegerOutput<>(ByteArrayCodec.INSTANCE), "NUMPAT".getBytes())).longValue(); } @@ -98,8 +98,8 @@ private static long numPat(RedisConnection connection) { private static long numSub(RedisConnection connection, String channel) { List pubsub; - if (connection instanceof LettuceConnection) { - pubsub = (List) ((LettuceConnection) connection).execute("PUBSUB", new ArrayOutput<>(ByteArrayCodec.INSTANCE), + if (connection instanceof LettuceConnection lettuceConnection) { + pubsub = (List) lettuceConnection.execute("PUBSUB", new ArrayOutput<>(ByteArrayCodec.INSTANCE), "NUMSUB".getBytes(), channel.getBytes()); } else { pubsub = (List) connection.execute("PUBSUB", "NUMSUB".getBytes(), channel.getBytes()); diff --git a/src/test/java/org/springframework/data/redis/listener/PubSubResubscribeTests.java b/src/test/java/org/springframework/data/redis/listener/PubSubResubscribeTests.java index 378c030499..540a619313 100644 --- a/src/test/java/org/springframework/data/redis/listener/PubSubResubscribeTests.java +++ b/src/test/java/org/springframework/data/redis/listener/PubSubResubscribeTests.java @@ -236,10 +236,10 @@ private static boolean clusterAvailable() { private static boolean isClusterAware(RedisConnectionFactory connectionFactory) { - if (connectionFactory instanceof LettuceConnectionFactory) { - return ((LettuceConnectionFactory) connectionFactory).isClusterAware(); - } else if (connectionFactory instanceof JedisConnectionFactory) { - return ((JedisConnectionFactory) connectionFactory).isRedisClusterAware(); + if (connectionFactory instanceof LettuceConnectionFactory lettuceConnectionFactory) { + return lettuceConnectionFactory.isClusterAware(); + } else if (connectionFactory instanceof JedisConnectionFactory jedisConnectionFactory) { + return jedisConnectionFactory.isRedisClusterAware(); } return false; } diff --git a/src/test/java/org/springframework/data/redis/listener/PubSubTests.java b/src/test/java/org/springframework/data/redis/listener/PubSubTests.java index 4f577dd447..84d60c82b9 100644 --- a/src/test/java/org/springframework/data/redis/listener/PubSubTests.java +++ b/src/test/java/org/springframework/data/redis/listener/PubSubTests.java @@ -170,10 +170,10 @@ void testStartListenersToNoSpecificChannelTest() { private static boolean isClusterAware(RedisConnectionFactory connectionFactory) { - if (connectionFactory instanceof LettuceConnectionFactory) { - return ((LettuceConnectionFactory) connectionFactory).isClusterAware(); - } else if (connectionFactory instanceof JedisConnectionFactory) { - return ((JedisConnectionFactory) connectionFactory).isRedisClusterAware(); + if (connectionFactory instanceof LettuceConnectionFactory lettuceConnectionFactory) { + return lettuceConnectionFactory.isClusterAware(); + } else if (connectionFactory instanceof JedisConnectionFactory jedisConnectionFactory) { + return jedisConnectionFactory.isRedisClusterAware(); } return false; } diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperIntegrationTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperIntegrationTests.java index 608132504c..c116dce5c5 100644 --- a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperIntegrationTests.java @@ -54,8 +54,8 @@ public class Jackson2HashMapperIntegrationTests { public Jackson2HashMapperIntegrationTests(RedisConnectionFactory factory) throws Exception { this.factory = factory; - if (factory instanceof InitializingBean) { - ((InitializingBean) factory).afterPropertiesSet(); + if (factory instanceof InitializingBean initializingBean) { + initializingBean.afterPropertiesSet(); } } diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/Person.java b/src/test/java/org/springframework/data/redis/repository/cdi/Person.java index 8083246095..00d23ff94b 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/Person.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/Person.java @@ -50,11 +50,9 @@ public void setName(String name) { public boolean equals(@Nullable Object o) { if (this == o) return true; - if (!(o instanceof Person)) + if (!(o instanceof Person person)) return false; - Person person = (Person) o; - if (id != null ? !id.equals(person.id) : person.id != null) return false; return name != null ? name.equals(person.name) : person.name == null; diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/RedisCdiDependenciesProducer.java b/src/test/java/org/springframework/data/redis/repository/cdi/RedisCdiDependenciesProducer.java index 88c310e7e4..5f9f8b32ec 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/RedisCdiDependenciesProducer.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/RedisCdiDependenciesProducer.java @@ -63,8 +63,8 @@ public RedisOperations redisOperationsProducerQualified(RedisOpe public void closeRedisOperations(@Disposes RedisOperations redisOperations) throws Exception { - if (redisOperations instanceof DisposableBean) { - ((DisposableBean) redisOperations).destroy(); + if (redisOperations instanceof DisposableBean disposableBean) { + disposableBean.destroy(); } } diff --git a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java index 2a6011d952..16daa677c8 100644 --- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java @@ -614,10 +614,9 @@ public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } - if (!(obj instanceof SimpleObject)) { + if (!(obj instanceof SimpleObject other)) { return false; } - SimpleObject other = (SimpleObject) obj; return nullSafeEquals(this.longValue, other.longValue); } } diff --git a/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java b/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java index 8df2d894f9..8f74c5a4f2 100644 --- a/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java @@ -395,9 +395,9 @@ private int getNumberOfPending(String stream, String group) { RedisConnection connection = connectionFactory.getConnection(); - if (connection instanceof LettuceConnection) { + if (connection instanceof LettuceConnection lettuceConnection) { - String value = ((List) ((LettuceConnection) connectionFactory.getConnection()).execute("XPENDING", + String value = ((List) lettuceConnection.execute("XPENDING", new NestedMultiOutput<>(StringCodec.UTF8), new byte[][] { stream.getBytes(), group.getBytes() })).get(0) .toString(); return NumberUtils.parseNumber(value, Integer.class); diff --git a/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java index e193f58618..df8992bb91 100644 --- a/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java @@ -119,14 +119,14 @@ void testPersist() throws Exception { @SuppressWarnings({ "unchecked", "rawtypes" }) private void populateBoundKey() { - if (keyOps instanceof Collection) { - ((Collection) keyOps).add("dummy"); - } else if (keyOps instanceof Map) { - ((Map) keyOps).put("dummy", "dummy"); - } else if (keyOps instanceof RedisAtomicInteger) { - ((RedisAtomicInteger) keyOps).set(42); - } else if (keyOps instanceof RedisAtomicLong) { - ((RedisAtomicLong) keyOps).set(42L); + if (keyOps instanceof Collection collection) { + collection.add("dummy"); + } else if (keyOps instanceof Map map) { + map.put("dummy", "dummy"); + } else if (keyOps instanceof RedisAtomicInteger redisAtomicInteger) { + redisAtomicInteger.set(42); + } else if (keyOps instanceof RedisAtomicLong redisAtomicLong) { + redisAtomicLong.set(42L); } } } diff --git a/src/test/java/org/springframework/data/redis/test/util/CollectionAwareComparator.java b/src/test/java/org/springframework/data/redis/test/util/CollectionAwareComparator.java index b150a16a72..c20657d209 100644 --- a/src/test/java/org/springframework/data/redis/test/util/CollectionAwareComparator.java +++ b/src/test/java/org/springframework/data/redis/test/util/CollectionAwareComparator.java @@ -34,10 +34,7 @@ public enum CollectionAwareComparator implements Comparator { @Override public int compare(Object o1, Object o2) { - if (o1 instanceof Collection && o2 instanceof Collection) { - - Collection c1 = (Collection) o1; - Collection c2 = (Collection) o2; + if (o1 instanceof Collection c1 && o2 instanceof Collection c2) { if (c1.size() != c2.size()) { return 1; From 3bc9423cd16ae2a960a699113b6c30aa735bd2e5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 20 Aug 2024 10:10:00 +0200 Subject: [PATCH 036/187] Polishing. Replace instanceof check with pattern variable in production code. Add missing Deprecated annotations. See #2967 Original pull request: #2971 --- .../redis/connection/BitFieldSubCommands.java | 18 ++++++------------ .../connection/RedisSocketConfiguration.java | 3 +-- .../RedisStandaloneConfiguration.java | 3 +-- ...RedisStaticMasterReplicaConfiguration.java | 3 +-- .../redis/connection/RedisStreamCommands.java | 3 +-- .../redis/connection/SentinelMasterId.java | 3 +-- .../connection/jedis/JedisConverters.java | 6 ++---- .../connection/jedis/StreamConverters.java | 3 +-- .../connection/lettuce/LettuceConverters.java | 7 ++----- .../LettucePoolingConnectionProvider.java | 3 +-- .../redis/connection/zset/DefaultTuple.java | 3 +-- .../data/redis/connection/zset/Weights.java | 3 +-- .../data/redis/core/BoundSetOperations.java | 2 ++ .../core/convert/GeoIndexedPropertyValue.java | 3 +-- .../core/index/RedisIndexDefinition.java | 3 +-- .../data/redis/domain/geo/BoundingBox.java | 4 +--- .../data/redis/domain/geo/GeoLocation.java | 5 +---- .../data/redis/domain/geo/GeoReference.java | 6 ++---- .../data/redis/hash/Jackson2HashMapper.java | 19 +++---------------- ...ReactiveRedisMessageListenerContainer.java | 11 +++++------ .../cdi/RedisKeyValueAdapterBean.java | 3 +-- ...DefaultStreamMessageListenerContainer.java | 4 +--- .../data/redis/listener/PubSubTests.java | 8 ++++---- ...sageListenerContainerIntegrationTests.java | 12 ++++++------ .../BoundKeyOperationsIntegrationTests.java | 8 ++++---- 25 files changed, 51 insertions(+), 95 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java b/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java index 4e4193e982..282893d6b6 100644 --- a/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java @@ -151,10 +151,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof BitFieldSubCommands)) { + if (!(o instanceof BitFieldSubCommands that)) { return false; } - BitFieldSubCommands that = (BitFieldSubCommands) o; return ObjectUtils.nullSafeEquals(subCommands, that.subCommands); } @@ -437,10 +436,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof Offset)) { + if (!(o instanceof Offset that)) { return false; } - Offset that = (Offset) o; if (offset != that.offset) { return false; } @@ -549,10 +547,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof BitFieldType)) { + if (!(o instanceof BitFieldType that)) { return false; } - BitFieldType that = (BitFieldType) o; if (signed != that.signed) { return false; } @@ -597,10 +594,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof AbstractBitFieldSubCommand)) { + if (!(o instanceof AbstractBitFieldSubCommand that)) { return false; } - AbstractBitFieldSubCommand that = (AbstractBitFieldSubCommand) o; if (!ObjectUtils.nullSafeEquals(getClass(), that.getClass())) { return false; } @@ -680,13 +676,12 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof BitFieldSet)) { + if (!(o instanceof BitFieldSet that)) { return false; } if (!super.equals(o)) { return false; } - BitFieldSet that = (BitFieldSet) o; if (value != that.value) { return false; } @@ -832,10 +827,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof BitFieldIncrBy)) { + if (!(o instanceof BitFieldIncrBy that)) { return false; } - BitFieldIncrBy that = (BitFieldIncrBy) o; if (value != that.value) { return false; } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java index 7e70cfdfd4..5e41c44a7b 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java @@ -108,10 +108,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof RedisSocketConfiguration)) { + if (!(o instanceof RedisSocketConfiguration that)) { return false; } - RedisSocketConfiguration that = (RedisSocketConfiguration) o; if (database != that.database) { return false; } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java index 0ec52a6b70..f7d8b2372a 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java @@ -137,10 +137,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof RedisStandaloneConfiguration)) { + if (!(o instanceof RedisStandaloneConfiguration that)) { return false; } - RedisStandaloneConfiguration that = (RedisStandaloneConfiguration) o; if (port != that.port) { return false; } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java index 6ed05903b1..1b60bf7a94 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java @@ -159,10 +159,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof RedisStaticMasterReplicaConfiguration)) { + if (!(o instanceof RedisStaticMasterReplicaConfiguration that)) { return false; } - RedisStaticMasterReplicaConfiguration that = (RedisStaticMasterReplicaConfiguration) o; if (database != that.database) { return false; } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java index a326106610..df9cbd9f52 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java @@ -246,10 +246,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof XAddOptions)) { + if (!(o instanceof XAddOptions that)) { return false; } - XAddOptions that = (XAddOptions) o; if (nomkstream != that.nomkstream) { return false; } diff --git a/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java b/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java index e5d72b171c..10954f4caa 100644 --- a/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java +++ b/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java @@ -51,10 +51,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof SentinelMasterId)) { + if (!(o instanceof SentinelMasterId that)) { return false; } - SentinelMasterId that = (SentinelMasterId) o; return ObjectUtils.nullSafeEquals(name, that.name); } 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 7a4c15613c..426d360575 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 @@ -760,9 +760,8 @@ private static GeoSearchParam getGeoSearchParam(GeoShape predicate, GeoSearchPar return param; } - if (predicate instanceof BoxShape) { + if (predicate instanceof BoxShape boxPredicate) { - BoxShape boxPredicate = (BoxShape) predicate; BoundingBox boundingBox = boxPredicate.getBoundingBox(); param.byBox(boundingBox.getWidth().getValue(), boundingBox.getHeight().getValue(), @@ -782,9 +781,8 @@ private static void configureGeoReference(GeoReference reference, GeoSea return; } - if (reference instanceof GeoReference.GeoCoordinateReference) { + if (reference instanceof GeoReference.GeoCoordinateReference coordinates) { - GeoReference.GeoCoordinateReference coordinates = (GeoReference.GeoCoordinateReference) reference; param.fromLonLat(coordinates.getLongitude(), coordinates.getLatitude()); return; } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/StreamConverters.java b/src/main/java/org/springframework/data/redis/connection/jedis/StreamConverters.java index 60d16a1f4a..f4b0c7b2ce 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/StreamConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/StreamConverters.java @@ -96,9 +96,8 @@ static List mapToList(Map map) { if (v instanceof StreamEntryID) { sources.add(v.toString()); - } else if (v instanceof StreamEntry) { + } else if (v instanceof StreamEntry streamEntry) { List entries = new ArrayList<>(2); - StreamEntry streamEntry = (StreamEntry) v; entries.add(streamEntry.getID().toString()); entries.add(streamEntry.getFields()); sources.add(entries); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java index 354217c548..450045a01e 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java @@ -863,9 +863,8 @@ static GeoSearch.GeoPredicate toGeoPredicate(GeoShape predicate) { return GeoSearch.byRadius(radius.getValue(), toGeoArgsUnit(radius.getMetric())); } - if (predicate instanceof BoxShape) { + if (predicate instanceof BoxShape boxPredicate) { - BoxShape boxPredicate = (BoxShape) predicate; BoundingBox boundingBox = boxPredicate.getBoundingBox(); return GeoSearch.byBox(boundingBox.getWidth().getValue(), boundingBox.getHeight().getValue(), @@ -881,9 +880,7 @@ static GeoSearch.GeoRef toGeoRef(GeoReference reference) { return GeoSearch.fromMember(((GeoMemberReference) reference).getMember()); } - if (reference instanceof GeoReference.GeoCoordinateReference) { - - GeoCoordinateReference coordinates = (GeoCoordinateReference) reference; + if (reference instanceof GeoCoordinateReference coordinates) { return GeoSearch.fromCoordinates(coordinates.getLongitude(), coordinates.getLatitude()); } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java index d82e88c3a0..5b888e8f0e 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java @@ -165,9 +165,8 @@ public void release(StatefulConnection connection) { private void discardIfNecessary(StatefulConnection connection) { - if (connection instanceof StatefulRedisConnection) { + if (connection instanceof StatefulRedisConnection redisConnection) { - StatefulRedisConnection redisConnection = (StatefulRedisConnection) connection; if (redisConnection.isMulti()) { redisConnection.async().discard(); } diff --git a/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java b/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java index bddd817e0a..4fa99efcef 100644 --- a/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java +++ b/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java @@ -58,9 +58,8 @@ public boolean equals(@Nullable Object obj) { return true; if (obj == null) return false; - if (!(obj instanceof DefaultTuple)) + if (!(obj instanceof DefaultTuple other)) return false; - DefaultTuple other = (DefaultTuple) obj; if (score == null) { if (other.score != null) return false; diff --git a/src/main/java/org/springframework/data/redis/connection/zset/Weights.java b/src/main/java/org/springframework/data/redis/connection/zset/Weights.java index a40045f274..1ecab0fe37 100644 --- a/src/main/java/org/springframework/data/redis/connection/zset/Weights.java +++ b/src/main/java/org/springframework/data/redis/connection/zset/Weights.java @@ -151,11 +151,10 @@ public boolean equals(@Nullable Object o) { return true; } - if (!(o instanceof Weights)) { + if (!(o instanceof Weights that)) { return false; } - Weights that = (Weights) o; return ObjectUtils.nullSafeEquals(this.weights, that.weights); } diff --git a/src/main/java/org/springframework/data/redis/core/BoundSetOperations.java b/src/main/java/org/springframework/data/redis/core/BoundSetOperations.java index b9d03ab655..5d333bbc0d 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundSetOperations.java @@ -185,6 +185,7 @@ public interface BoundSetOperations extends BoundKeyOperations { * @deprecated since 3.0, use {@link #difference(Object)} instead to follow a consistent method naming scheme. */ @Nullable + @Deprecated(since = "3.0") default Set diff(K key) { return difference(key); } @@ -209,6 +210,7 @@ default Set diff(K key) { * @deprecated since 3.0, use {@link #difference(Collection)} instead to follow a consistent method naming scheme. */ @Nullable + @Deprecated(since = "3.0") default Set diff(Collection keys) { return difference(keys); } diff --git a/src/main/java/org/springframework/data/redis/core/convert/GeoIndexedPropertyValue.java b/src/main/java/org/springframework/data/redis/core/convert/GeoIndexedPropertyValue.java index 979d576a08..e45af221bc 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/GeoIndexedPropertyValue.java +++ b/src/main/java/org/springframework/data/redis/core/convert/GeoIndexedPropertyValue.java @@ -74,11 +74,10 @@ public boolean equals(@Nullable Object o) { return true; } - if (!(o instanceof GeoIndexedPropertyValue)) { + if (!(o instanceof GeoIndexedPropertyValue that)) { return false; } - GeoIndexedPropertyValue that = (GeoIndexedPropertyValue) o; if (!ObjectUtils.nullSafeEquals(keyspace, that.keyspace)) { return false; } diff --git a/src/main/java/org/springframework/data/redis/core/index/RedisIndexDefinition.java b/src/main/java/org/springframework/data/redis/core/index/RedisIndexDefinition.java index 122659a12c..8e0e522fe3 100644 --- a/src/main/java/org/springframework/data/redis/core/index/RedisIndexDefinition.java +++ b/src/main/java/org/springframework/data/redis/core/index/RedisIndexDefinition.java @@ -105,10 +105,9 @@ public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } - if (!(obj instanceof RedisIndexDefinition)) { + if (!(obj instanceof RedisIndexDefinition that)) { return false; } - RedisIndexDefinition that = (RedisIndexDefinition) obj; if (!ObjectUtils.nullSafeEquals(this.keyspace, that.keyspace)) { return false; diff --git a/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java b/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java index 4d00e271d1..1807832b65 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java @@ -18,7 +18,6 @@ import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metric; import org.springframework.data.geo.Shape; -import org.springframework.data.redis.connection.RedisGeoCommands; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -94,10 +93,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof BoundingBox)) { + if (!(o instanceof BoundingBox that)) { return false; } - BoundingBox that = (BoundingBox) o; if (!ObjectUtils.nullSafeEquals(width, that.width)) { return false; } diff --git a/src/main/java/org/springframework/data/redis/domain/geo/GeoLocation.java b/src/main/java/org/springframework/data/redis/domain/geo/GeoLocation.java index bd570afab6..ee60b5ea4e 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/GeoLocation.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/GeoLocation.java @@ -16,7 +16,6 @@ package org.springframework.data.redis.domain.geo; import org.springframework.data.geo.Point; -import org.springframework.data.redis.connection.RedisGeoCommands; import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; @@ -49,12 +48,10 @@ public boolean equals(@Nullable Object o) { return true; } - if (!(o instanceof GeoLocation)) { + if (!(o instanceof GeoLocation that)) { return false; } - GeoLocation that = (GeoLocation) o; - if (!ObjectUtils.nullSafeEquals(name, that.name)) { return false; } diff --git a/src/main/java/org/springframework/data/redis/domain/geo/GeoReference.java b/src/main/java/org/springframework/data/redis/domain/geo/GeoReference.java index b3d728e0b6..004f4c5c3b 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/GeoReference.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/GeoReference.java @@ -133,10 +133,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof GeoReference.GeoMemberReference)) { + if (!(o instanceof GeoMemberReference that)) { return false; } - GeoMemberReference that = (GeoMemberReference) o; return ObjectUtils.nullSafeEquals(member, that.member); } @@ -178,10 +177,9 @@ public boolean equals(@Nullable Object o) { if (this == o) { return true; } - if (!(o instanceof GeoReference.GeoCoordinateReference)) { + if (!(o instanceof GeoCoordinateReference that)) { return false; } - GeoCoordinateReference that = (GeoCoordinateReference) o; if (longitude != that.longitude) { return false; } diff --git a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java index e33243b6b0..e7dcb37b1c 100644 --- a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java @@ -15,23 +15,12 @@ */ package org.springframework.data.redis.hash; -import static com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping.EVERYTHING; +import static com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping.*; import java.io.IOException; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; import org.springframework.data.mapping.MappingException; import org.springframework.data.redis.support.collections.CollectionUtils; @@ -379,13 +368,11 @@ private void doFlatten(String propertyPrefix, Iterator> private void flattenElement(String propertyPrefix, Object source, Map resultMap) { - if (!(source instanceof JsonNode)) { + if (!(source instanceof JsonNode element)) { resultMap.put(propertyPrefix, source); return; } - JsonNode element = (JsonNode) source; - if (element.isArray()) { Iterator nodes = element.elements(); diff --git a/src/main/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainer.java index 58903a5a2e..c23a75b9ed 100644 --- a/src/main/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainer.java @@ -135,8 +135,8 @@ private Mono doDestroy() { */ public Collection getActiveSubscriptions() { - return subscriptions.entrySet().stream().filter(entry -> entry.getValue().hasRegistration()) - .map(Map.Entry::getKey).collect(Collectors.toList()); + return subscriptions.entrySet().stream().filter(entry -> entry.getValue().hasRegistration()).map(Map.Entry::getKey) + .collect(Collectors.toList()); } /** @@ -295,8 +295,7 @@ public Flux> receive(Iterable topics, Seri } return doReceive(channelSerializer, messageSerializer, - getRequiredConnection().pubSubCommands().createSubscription(subscriptionListener), patterns, - channels); + getRequiredConnection().pubSubCommands().createSubscription(subscriptionListener), patterns, channels); } private Flux> doReceive(SerializationPair channelSerializer, @@ -361,7 +360,7 @@ public Mono>> receiveLater(Iterable t return doReceiveLater(channelSerializer, messageSerializer, getRequiredConnection().pubSubCommands().createSubscription(readyListener), patterns, channels) - .delayUntil(it -> readyListener.getTrigger()); + .delayUntil(it -> readyListener.getTrigger()); }); } @@ -441,7 +440,7 @@ private ByteBuffer[] getTargets(Iterable topics, Class class private Message readMessage(RedisElementReader channelSerializer, RedisElementReader messageSerializer, Message message) { - if (message instanceof PatternMessage) { + if (message instanceof PatternMessage) { PatternMessage patternMessage = (PatternMessage) message; diff --git a/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueAdapterBean.java b/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueAdapterBean.java index 9a1a8988b8..5c93d1009c 100644 --- a/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueAdapterBean.java +++ b/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueAdapterBean.java @@ -70,8 +70,7 @@ private Type getBeanType() { return type; } - if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; + if (type instanceof ParameterizedType parameterizedType) { if (parameterizedType.getRawType() instanceof Class && RedisOperations.class.isAssignableFrom((Class) parameterizedType.getRawType())) { return type; diff --git a/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainer.java index 25cd0d4b13..bd727ce89c 100644 --- a/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainer.java @@ -224,9 +224,7 @@ private Function> getReadFunction(StreamReadRequest byte[] rawKey = ((RedisSerializer) template.getKeySerializer()) .serialize(streamRequest.getStreamOffset().getKey()); - if (streamRequest instanceof StreamMessageListenerContainer.ConsumerStreamReadRequest) { - - ConsumerStreamReadRequest consumerStreamRequest = (ConsumerStreamReadRequest) streamRequest; + if (streamRequest instanceof ConsumerStreamReadRequest consumerStreamRequest) { StreamReadOptions readOptions = consumerStreamRequest.isAutoAcknowledge() ? this.readOptions.autoAcknowledge() : this.readOptions; diff --git a/src/test/java/org/springframework/data/redis/listener/PubSubTests.java b/src/test/java/org/springframework/data/redis/listener/PubSubTests.java index 84d60c82b9..38413dc5d9 100644 --- a/src/test/java/org/springframework/data/redis/listener/PubSubTests.java +++ b/src/test/java/org/springframework/data/redis/listener/PubSubTests.java @@ -170,10 +170,10 @@ void testStartListenersToNoSpecificChannelTest() { private static boolean isClusterAware(RedisConnectionFactory connectionFactory) { - if (connectionFactory instanceof LettuceConnectionFactory lettuceConnectionFactory) { - return lettuceConnectionFactory.isClusterAware(); - } else if (connectionFactory instanceof JedisConnectionFactory jedisConnectionFactory) { - return jedisConnectionFactory.isRedisClusterAware(); + if (connectionFactory instanceof LettuceConnectionFactory lettuce) { + return lettuce.isClusterAware(); + } else if (connectionFactory instanceof JedisConnectionFactory jedis) { + return jedis.isRedisClusterAware(); } return false; } diff --git a/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java b/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java index 8f74c5a4f2..4e5d620ccf 100644 --- a/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java @@ -15,7 +15,10 @@ */ package org.springframework.data.redis.stream; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; + +import io.lettuce.core.codec.StringCodec; +import io.lettuce.core.output.NestedMultiOutput; import java.time.Duration; import java.util.Collections; @@ -48,9 +51,6 @@ import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.util.NumberUtils; -import io.lettuce.core.codec.StringCodec; -import io.lettuce.core.output.NestedMultiOutput; - /** * Integration tests for {@link StreamMessageListenerContainer}. * @@ -395,9 +395,9 @@ private int getNumberOfPending(String stream, String group) { RedisConnection connection = connectionFactory.getConnection(); - if (connection instanceof LettuceConnection lettuceConnection) { + if (connection instanceof LettuceConnection lettuce) { - String value = ((List) lettuceConnection.execute("XPENDING", + String value = ((List) lettuce.execute("XPENDING", new NestedMultiOutput<>(StringCodec.UTF8), new byte[][] { stream.getBytes(), group.getBytes() })).get(0) .toString(); return NumberUtils.parseNumber(value, Integer.class); diff --git a/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java index df8992bb91..826247c56f 100644 --- a/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java @@ -123,10 +123,10 @@ private void populateBoundKey() { collection.add("dummy"); } else if (keyOps instanceof Map map) { map.put("dummy", "dummy"); - } else if (keyOps instanceof RedisAtomicInteger redisAtomicInteger) { - redisAtomicInteger.set(42); - } else if (keyOps instanceof RedisAtomicLong redisAtomicLong) { - redisAtomicLong.set(42L); + } else if (keyOps instanceof RedisAtomicInteger atomic) { + atomic.set(42); + } else if (keyOps instanceof RedisAtomicLong atomic) { + atomic.set(42L); } } } From e56e8c59b22f11e206f4e3d97061f3cc13bc9677 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 20 Aug 2024 10:30:24 +0200 Subject: [PATCH 037/187] Add `@Serial` annotation to all `serialVersionUID` fields. Closes #2973 --- .../springframework/data/redis/ClusterRedirectException.java | 3 +++ .../data/redis/ClusterStateFailureException.java | 3 +++ .../data/redis/TooManyClusterRedirectionsException.java | 3 +++ .../connection/ClusterCommandExecutionFailureException.java | 3 ++- .../org/springframework/data/redis/domain/geo/BoundingBox.java | 3 +++ .../data/redis/support/atomic/RedisAtomicDouble.java | 2 ++ .../data/redis/support/atomic/RedisAtomicInteger.java | 2 ++ .../data/redis/support/atomic/RedisAtomicLong.java | 2 ++ 8 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/redis/ClusterRedirectException.java b/src/main/java/org/springframework/data/redis/ClusterRedirectException.java index 79a5f0632d..3f8abc5811 100644 --- a/src/main/java/org/springframework/data/redis/ClusterRedirectException.java +++ b/src/main/java/org/springframework/data/redis/ClusterRedirectException.java @@ -15,6 +15,8 @@ */ package org.springframework.data.redis; +import java.io.Serial; + import org.springframework.dao.DataRetrievalFailureException; /** @@ -27,6 +29,7 @@ */ public class ClusterRedirectException extends DataRetrievalFailureException { + @Serial private static final long serialVersionUID = -857075813794333965L; private final int slot; diff --git a/src/main/java/org/springframework/data/redis/ClusterStateFailureException.java b/src/main/java/org/springframework/data/redis/ClusterStateFailureException.java index 61ccabde15..d3867139c0 100644 --- a/src/main/java/org/springframework/data/redis/ClusterStateFailureException.java +++ b/src/main/java/org/springframework/data/redis/ClusterStateFailureException.java @@ -15,6 +15,8 @@ */ package org.springframework.data.redis; +import java.io.Serial; + import org.springframework.dao.DataAccessResourceFailureException; /** @@ -28,6 +30,7 @@ */ public class ClusterStateFailureException extends DataAccessResourceFailureException { + @Serial private static final long serialVersionUID = 333399051713240852L; /** diff --git a/src/main/java/org/springframework/data/redis/TooManyClusterRedirectionsException.java b/src/main/java/org/springframework/data/redis/TooManyClusterRedirectionsException.java index 6a40b28f00..db4b3026b4 100644 --- a/src/main/java/org/springframework/data/redis/TooManyClusterRedirectionsException.java +++ b/src/main/java/org/springframework/data/redis/TooManyClusterRedirectionsException.java @@ -15,6 +15,8 @@ */ package org.springframework.data.redis; +import java.io.Serial; + import org.springframework.dao.DataRetrievalFailureException; /** @@ -25,6 +27,7 @@ */ public class TooManyClusterRedirectionsException extends DataRetrievalFailureException { + @Serial private static final long serialVersionUID = -2818933672669154328L; /** diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutionFailureException.java b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutionFailureException.java index 7a786b63f9..851a42cd1c 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutionFailureException.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutionFailureException.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.connection; +import java.io.Serial; import java.util.Collections; import java.util.List; @@ -29,7 +30,7 @@ */ public class ClusterCommandExecutionFailureException extends UncategorizedDataAccessException { - private static final long serialVersionUID = 5727044227040368955L; + @Serial private static final long serialVersionUID = 5727044227040368955L; /** * Creates new {@link ClusterCommandExecutionFailureException}. diff --git a/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java b/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java index 1807832b65..3411917290 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java @@ -15,6 +15,8 @@ */ package org.springframework.data.redis.domain.geo; +import java.io.Serial; + import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metric; import org.springframework.data.geo.Shape; @@ -30,6 +32,7 @@ */ public class BoundingBox implements Shape { + @Serial private static final long serialVersionUID = 5215611530535947924L; private final Distance width; diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java index 3cef647aa9..eb6c930145 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.support.atomic; +import java.io.Serial; import java.io.Serializable; import java.util.Date; import java.util.concurrent.TimeUnit; @@ -46,6 +47,7 @@ */ public class RedisAtomicDouble extends Number implements Serializable, BoundKeyOperations { + @Serial private static final long serialVersionUID = 1L; private volatile String key; diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java index dc4289f1d2..04f43f8d73 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.support.atomic; +import java.io.Serial; import java.io.Serializable; import java.util.Date; import java.util.concurrent.TimeUnit; @@ -47,6 +48,7 @@ */ public class RedisAtomicInteger extends Number implements Serializable, BoundKeyOperations { + @Serial private static final long serialVersionUID = 1L; private volatile String key; diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java index 9e92b0d873..f661d5dc3d 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.support.atomic; +import java.io.Serial; import java.io.Serializable; import java.util.Date; import java.util.concurrent.TimeUnit; @@ -48,6 +49,7 @@ */ public class RedisAtomicLong extends Number implements Serializable, BoundKeyOperations { + @Serial private static final long serialVersionUID = 1L; private volatile String key; From a58bfd83c3a6979e55eeb0d6630d365611cc5bc8 Mon Sep 17 00:00:00 2001 From: Lee Jaeheon Date: Tue, 13 Aug 2024 23:16:10 +0900 Subject: [PATCH 038/187] Add `getFirst(K key)` and `getLast(K key)` methods to `ListOperations`. Original pull request: #2966 Closes #2937 --- .../data/redis/core/ListOperations.java | 31 +++++++++++++++++++ ...OperationsIntegrationIntegrationTests.java | 29 +++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/main/java/org/springframework/data/redis/core/ListOperations.java b/src/main/java/org/springframework/data/redis/core/ListOperations.java index f5c8948385..853f8863cd 100644 --- a/src/main/java/org/springframework/data/redis/core/ListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ListOperations.java @@ -34,6 +34,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author dengliming + * @author Lee Jaeheon */ public interface ListOperations { @@ -559,5 +560,35 @@ default V rightPopAndLeftPush(K sourceKey, K destinationKey, Duration timeout) { return rightPopAndLeftPush(sourceKey, destinationKey, TimeoutUtils.toSeconds(timeout), TimeUnit.SECONDS); } + /** + * Returns the first element from list at {@code key}. + * + * @implSpec + * The implementation in this interface returns the result of calling {@code index(key, 0)}. + * + * @param key must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + */ + @Nullable + default V getFirst(K key) { + return index(key, 0); + } + + /** + * Returns the last element from list at {@code key}. + * + * @implSpec + * If the result of calling {@code size(key)} is not null, The implementation in this interface returns the + * result of calling {@code index(key, size - 1)}. Otherwise, it returns null. + * + * @param key must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + */ + @Nullable + default V getLast(K key) { + Long size = size(key); + return size != null ? index(key, size - 1) : null; + } + RedisOperations getOperations(); } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java index 559551a4b0..1280bfd803 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java @@ -40,6 +40,7 @@ * @author Jennifer Hickey * @author Thomas Darimont * @author Christoph Strobl + * @author Lee Jaeheon * @param Key test * @param Value test */ @@ -376,4 +377,32 @@ void lastIndexOf() { assertThat(listOps.rightPush(key, v3)).isEqualTo(Long.valueOf(4)); assertThat(listOps.lastIndexOf(key, v1)).isEqualTo(2); } + + @ParameterizedRedisTest // GH-2937 + void getFirst() { + + K key = keyFactory.instance(); + V v1 = valueFactory.instance(); + V v2 = valueFactory.instance(); + V v3 = valueFactory.instance(); + + listOps.rightPush(key, v1); + listOps.rightPush(key, v2); + listOps.rightPush(key, v3); + assertThat(listOps.getFirst(key)).isEqualTo(v1); + } + + @ParameterizedRedisTest // GH-2937 + void getLast() { + + K key = keyFactory.instance(); + V v1 = valueFactory.instance(); + V v2 = valueFactory.instance(); + V v3 = valueFactory.instance(); + + listOps.rightPush(key, v1); + listOps.rightPush(key, v2); + listOps.rightPush(key, v3); + assertThat(listOps.getLast(key)).isEqualTo(v3); + } } From bd20c6d6f7e3b37591680eeafc3b8d100e1ad0e8 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 20 Aug 2024 15:16:53 +0200 Subject: [PATCH 039/187] Polishing. Add getFirst/getLast to Reactive and Bound Operations. Simplify getLast implementation. Reorder methods, tweak Javadoc, add since tags. Original pull request: #2966 See #2937 --- .../data/redis/core/BoundListOperations.java | 18 ++++++ .../data/redis/core/ListOperations.java | 60 +++++++++---------- .../redis/core/ReactiveListOperations.java | 25 ++++++++ .../support/collections/DefaultRedisList.java | 4 +- ...OperationsIntegrationIntegrationTests.java | 58 +++++++++--------- ...eactiveListOperationsIntegrationTests.java | 56 ++++++++++------- 6 files changed, 136 insertions(+), 85 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java index 9c429debac..a33fab90d7 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java @@ -212,6 +212,24 @@ public interface BoundListOperations extends BoundKeyOperations { @Nullable Long remove(long count, Object value); + /** + * Returns the first element from the list at the bound {@code key}. + * + * @return {@literal null} when used in pipeline / transaction. + * @since 3.4 + */ + @Nullable + V getFirst(); + + /** + * Returns the last element from the list at the bound {@code key}. + * + * @return {@literal null} when used in pipeline / transaction. + * @since 3.4 + */ + @Nullable + V getLast(); + /** * Get element at {@code index} form list at the bound key. * diff --git a/src/main/java/org/springframework/data/redis/core/ListOperations.java b/src/main/java/org/springframework/data/redis/core/ListOperations.java index 853f8863cd..5a946c62db 100644 --- a/src/main/java/org/springframework/data/redis/core/ListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ListOperations.java @@ -368,6 +368,30 @@ default V move(K sourceKey, Direction from, K destinationKey, Direction to, Dura @Nullable Long remove(K key, long count, Object value); + /** + * Returns the first element from the list at {@code key}. + * + * @param key must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.4 + */ + @Nullable + default V getFirst(K key) { + return index(key, 0); + } + + /** + * Returns the last element from the list at {@code key}. + * + * @param key must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.4 + */ + @Nullable + default V getLast(K key) { + return index(key, -1); + } + /** * Get element at {@code index} form list at {@code key}. * @@ -526,7 +550,8 @@ default V rightPop(K key, Duration timeout) { V rightPopAndLeftPush(K sourceKey, K destinationKey); /** - * Remove the last element from list at {@code sourceKey}, append it to {@code destinationKey} and return its value.
+ * Remove the last element from list at {@code sourceKey}, append it to {@code destinationKey} and return its + * value.
* Blocks connection until element available or {@code timeout} reached. * * @param sourceKey must not be {@literal null}. @@ -540,7 +565,8 @@ default V rightPop(K key, Duration timeout) { V rightPopAndLeftPush(K sourceKey, K destinationKey, long timeout, TimeUnit unit); /** - * Remove the last element from list at {@code sourceKey}, append it to {@code destinationKey} and return its value.
+ * Remove the last element from list at {@code sourceKey}, append it to {@code destinationKey} and return its + * value.
* Blocks connection until element available or {@code timeout} reached. * * @param sourceKey must not be {@literal null}. @@ -560,35 +586,5 @@ default V rightPopAndLeftPush(K sourceKey, K destinationKey, Duration timeout) { return rightPopAndLeftPush(sourceKey, destinationKey, TimeoutUtils.toSeconds(timeout), TimeUnit.SECONDS); } - /** - * Returns the first element from list at {@code key}. - * - * @implSpec - * The implementation in this interface returns the result of calling {@code index(key, 0)}. - * - * @param key must not be {@literal null}. - * @return {@literal null} when used in pipeline / transaction. - */ - @Nullable - default V getFirst(K key) { - return index(key, 0); - } - - /** - * Returns the last element from list at {@code key}. - * - * @implSpec - * If the result of calling {@code size(key)} is not null, The implementation in this interface returns the - * result of calling {@code index(key, size - 1)}. Otherwise, it returns null. - * - * @param key must not be {@literal null}. - * @return {@literal null} when used in pipeline / transaction. - */ - @Nullable - default V getLast(K key) { - Long size = size(key); - return size != null ? index(key, size - 1) : null; - } - RedisOperations getOperations(); } diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java index 6a701c2eac..73ff7efa8c 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java @@ -26,6 +26,7 @@ import org.springframework.data.redis.core.ListOperations.MoveFrom; import org.springframework.data.redis.core.ListOperations.MoveTo; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -276,6 +277,30 @@ default Mono move(MoveFrom from, MoveTo to, Duration timeout) { */ Mono remove(K key, long count, Object value); + /** + * Returns the first element from the list at {@code key}. + * + * @param key must not be {@literal null}. + * @return + * @since 3.4 + */ + @Nullable + default Mono getFirst(K key) { + return index(key, 0); + } + + /** + * Returns the last element from the list at {@code key}. + * + * @param key must not be {@literal null}. + * @return + * @since 3.4 + */ + @Nullable + default Mono getLast(K key) { + return index(key, -1); + } + /** * Get element at {@code index} form list at {@code key}. * diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java index fd2da300cf..8c9aa5f8d0 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java @@ -349,7 +349,7 @@ public boolean offer(E element) { @Override @Nullable public E peek() { - return listOps.index(0); + return listOps.getFirst(); } @Override @@ -426,7 +426,7 @@ public E peekFirst() { @Override @Nullable public E peekLast() { - return listOps.index(-1); + return listOps.getLast(); } @Override diff --git a/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java index 1280bfd803..7ee0b5d403 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java @@ -56,8 +56,7 @@ public class DefaultListOperationsIntegrationIntegrationTests { private final ListOperations listOps; public DefaultListOperationsIntegrationIntegrationTests(RedisTemplate redisTemplate, - ObjectFactory keyFactory, - ObjectFactory valueFactory) { + ObjectFactory keyFactory, ObjectFactory valueFactory) { this.redisTemplate = redisTemplate; this.keyFactory = keyFactory; @@ -347,62 +346,63 @@ void moveWithTimeout() { assertThat(listOps.range(target, 0, -1)).containsExactly(v4, v1); } - @ParameterizedRedisTest // DATAREDIS-1196 - @EnabledOnCommand("LPOS") - void indexOf() { + @ParameterizedRedisTest // GH-2937 + void getFirst() { K key = keyFactory.instance(); V v1 = valueFactory.instance(); V v2 = valueFactory.instance(); V v3 = valueFactory.instance(); - assertThat(listOps.rightPush(key, v1)).isEqualTo(Long.valueOf(1)); - assertThat(listOps.rightPush(key, v2)).isEqualTo(Long.valueOf(2)); - assertThat(listOps.rightPush(key, v1, v3)).isEqualTo(Long.valueOf(3)); - assertThat(listOps.indexOf(key, v1)).isEqualTo(0); + listOps.rightPush(key, v1); + listOps.rightPush(key, v2); + listOps.rightPush(key, v3); + assertThat(listOps.getFirst(key)).isEqualTo(v1); } - @ParameterizedRedisTest // DATAREDIS-1196 - @EnabledOnCommand("LPOS") - void lastIndexOf() { + @ParameterizedRedisTest // GH-2937 + void getLast() { K key = keyFactory.instance(); V v1 = valueFactory.instance(); V v2 = valueFactory.instance(); V v3 = valueFactory.instance(); - assertThat(listOps.rightPush(key, v1)).isEqualTo(Long.valueOf(1)); - assertThat(listOps.rightPush(key, v2)).isEqualTo(Long.valueOf(2)); - assertThat(listOps.rightPush(key, v1)).isEqualTo(Long.valueOf(3)); - assertThat(listOps.rightPush(key, v3)).isEqualTo(Long.valueOf(4)); - assertThat(listOps.lastIndexOf(key, v1)).isEqualTo(2); + listOps.rightPush(key, v1); + listOps.rightPush(key, v2); + listOps.rightPush(key, v3); + assertThat(listOps.getLast(key)).isEqualTo(v3); } - @ParameterizedRedisTest // GH-2937 - void getFirst() { + @ParameterizedRedisTest // DATAREDIS-1196 + @EnabledOnCommand("LPOS") + void indexOf() { K key = keyFactory.instance(); V v1 = valueFactory.instance(); V v2 = valueFactory.instance(); V v3 = valueFactory.instance(); - listOps.rightPush(key, v1); - listOps.rightPush(key, v2); - listOps.rightPush(key, v3); - assertThat(listOps.getFirst(key)).isEqualTo(v1); + assertThat(listOps.rightPush(key, v1)).isEqualTo(Long.valueOf(1)); + assertThat(listOps.rightPush(key, v2)).isEqualTo(Long.valueOf(2)); + assertThat(listOps.rightPush(key, v1, v3)).isEqualTo(Long.valueOf(3)); + assertThat(listOps.indexOf(key, v1)).isEqualTo(0); } - @ParameterizedRedisTest // GH-2937 - void getLast() { + @ParameterizedRedisTest // DATAREDIS-1196 + @EnabledOnCommand("LPOS") + void lastIndexOf() { K key = keyFactory.instance(); V v1 = valueFactory.instance(); V v2 = valueFactory.instance(); V v3 = valueFactory.instance(); - listOps.rightPush(key, v1); - listOps.rightPush(key, v2); - listOps.rightPush(key, v3); - assertThat(listOps.getLast(key)).isEqualTo(v3); + assertThat(listOps.rightPush(key, v1)).isEqualTo(Long.valueOf(1)); + assertThat(listOps.rightPush(key, v2)).isEqualTo(Long.valueOf(2)); + assertThat(listOps.rightPush(key, v1)).isEqualTo(Long.valueOf(3)); + assertThat(listOps.rightPush(key, v3)).isEqualTo(Long.valueOf(4)); + assertThat(listOps.lastIndexOf(key, v1)).isEqualTo(2); } + } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java index 92fcd6d783..34eaf7cb5e 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java @@ -15,8 +15,10 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; + +import reactor.test.StepVerifier; import java.time.Duration; import java.util.Collection; @@ -33,8 +35,6 @@ import org.springframework.data.redis.test.extension.parametrized.MethodSource; import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest; -import reactor.test.StepVerifier; - /** * Integration tests for {@link DefaultReactiveListOperations}. * @@ -417,6 +417,32 @@ void index() { listOperations.index(key, 1).as(StepVerifier::create).expectNext(value2).verifyComplete(); } + @ParameterizedRedisTest // GH-2937 + void getFirst() { + + K key = keyFactory.instance(); + V v1 = valueFactory.instance(); + V v2 = valueFactory.instance(); + V v3 = valueFactory.instance(); + + listOperations.rightPushAll(key, v1, v2, v3).as(StepVerifier::create).expectNext(3L).verifyComplete(); + + listOperations.getFirst(key).as(StepVerifier::create).expectNext(v1).verifyComplete(); + } + + @ParameterizedRedisTest // GH-2937 + void getLast() { + + K key = keyFactory.instance(); + V v1 = valueFactory.instance(); + V v2 = valueFactory.instance(); + V v3 = valueFactory.instance(); + + listOperations.rightPushAll(key, v1, v2, v3).as(StepVerifier::create).expectNext(3L).verifyComplete(); + + listOperations.getLast(key).as(StepVerifier::create).expectNext(v3).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-1196 @EnabledOnCommand("LPOS") void indexOf() { @@ -469,16 +495,9 @@ void leftPopWithCount() { V value2 = valueFactory.instance(); V value3 = valueFactory.instance(); - listOperations.leftPushAll(key, value1, value2, value3) - .as(StepVerifier::create) - .expectNext(3L) - .verifyComplete(); + listOperations.leftPushAll(key, value1, value2, value3).as(StepVerifier::create).expectNext(3L).verifyComplete(); - listOperations.leftPop(key, 2) - .as(StepVerifier::create) - .expectNext(value3) - .expectNext(value2) - .verifyComplete(); + listOperations.leftPop(key, 2).as(StepVerifier::create).expectNext(value3).expectNext(value2).verifyComplete(); } @ParameterizedRedisTest // DATAREDIS-602 @@ -505,16 +524,9 @@ void rightPopWithCount() { V value2 = valueFactory.instance(); V value3 = valueFactory.instance(); - listOperations.rightPushAll(key, value3, value2, value1) - .as(StepVerifier::create) - .expectNext(3L) - .verifyComplete(); + listOperations.rightPushAll(key, value3, value2, value1).as(StepVerifier::create).expectNext(3L).verifyComplete(); - listOperations.rightPop(key, 2) - .as(StepVerifier::create) - .expectNext(value1) - .expectNext(value2) - .verifyComplete(); + listOperations.rightPop(key, 2).as(StepVerifier::create).expectNext(value1).expectNext(value2).verifyComplete(); } @ParameterizedRedisTest // DATAREDIS-602 From 9d716ff33aa590f49a4f61a8049805e61a353fc7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 21 Aug 2024 09:28:22 +0200 Subject: [PATCH 040/187] Update GitHub Actions. See #2912 --- .github/workflows/project.yml | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml index 606226523e..a5f764579a 100644 --- a/.github/workflows/project.yml +++ b/.github/workflows/project.yml @@ -13,35 +13,28 @@ on: jobs: Inbox: runs-on: ubuntu-latest - if: github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request == null + if: github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request == null && !contains(join(github.event.issue.labels.*.name, ', '), 'dependency-upgrade') && !contains(github.event.issue.title, 'Release ') steps: - name: Create or Update Issue Card - uses: peter-evans/create-or-update-project-card@v1.1.2 + uses: actions/add-to-project@v1.0.2 with: - project-name: 'Spring Data' - column-name: 'Inbox' - project-location: 'spring-projects' - token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }} + project-url: https://github.com/orgs/spring-projects/projects/25 + github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }} Pull-Request: runs-on: ubuntu-latest if: github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request != null steps: - name: Create or Update Pull Request Card - uses: peter-evans/create-or-update-project-card@v1.1.2 + uses: actions/add-to-project@v1.0.2 with: - project-name: 'Spring Data' - column-name: 'Review pending' - project-location: 'spring-projects' - issue-number: ${{ github.event.pull_request.number }} - token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }} + project-url: https://github.com/orgs/spring-projects/projects/25 + github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }} Feedback-Provided: runs-on: ubuntu-latest if: github.repository_owner == 'spring-projects' && github.event_name == 'issue_comment' && github.event.action == 'created' && github.actor != 'spring-projects-issues' && github.event.pull_request == null && github.event.issue.state == 'open' && contains(toJSON(github.event.issue.labels), 'waiting-for-feedback') steps: - name: Update Project Card - uses: peter-evans/create-or-update-project-card@v1.1.2 + uses: actions/add-to-project@v1.0.2 with: - project-name: 'Spring Data' - column-name: 'Feedback provided' - project-location: 'spring-projects' - token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }} + project-url: https://github.com/orgs/spring-projects/projects/25 + github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }} From 5fb06655d9761c2f1383da5ac69199993636faeb Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 22 Aug 2024 15:41:10 +0200 Subject: [PATCH 041/187] Upgrade to Jedis 5.1.5. Closes: #2976 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 792fe40817..09bebeb5bd 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 1.4.20 2.11.1 6.4.0.RELEASE - 5.1.4 + 5.1.5 1.01 4.1.107.Final spring.data.redis From bea01ef36cf439542279f7ddaa344a1b726767f8 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 28 Aug 2024 12:27:20 +0200 Subject: [PATCH 042/187] Fix error deserializing enum values with flattening Jackson2HashMapper. This commit makes sure the mapper does not attempt to read type alias when deserializing enums. Original pull request: #2980 Closes #2979 --- .../data/redis/hash/Jackson2HashMapper.java | 2 +- .../mapping/Jackson2HashMapperUnitTests.java | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java index e7dcb37b1c..7b5ca20543 100644 --- a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java @@ -177,7 +177,7 @@ public boolean useForType(JavaType type) { return false; } - if (flatten && type.isTypeOrSubTypeOf(Number.class)) { + if (flatten && (type.isTypeOrSubTypeOf(Number.class) || type.isEnumType())) { return false; } diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java index 4ca104122f..9b0735e6bd 100644 --- a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java @@ -203,6 +203,15 @@ void bigDecimalShouldBeTreatedCorrectly() { assertBackAndForwardMapping(source); } + @Test // GH-2979 + void enumsShouldBeTreatedCorrectly() { + + WithEnumValue source = new WithEnumValue(); + source.value = SpringDataEnum.REDIS; + + assertBackAndForwardMapping(source); + } + public static class WithList { List strings; @@ -452,4 +461,38 @@ public int hashCode() { return Objects.hash(getValue()); } } + + enum SpringDataEnum { + COMMONS, REDIS + } + + static class WithEnumValue { + + SpringDataEnum value; + + public SpringDataEnum getValue() { + return value; + } + + public void setValue(SpringDataEnum value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WithEnumValue that = (WithEnumValue) o; + return value == that.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + } } From bfa62bf31d591a0eb51cb6ba20c6f8d4f5bddd9f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 30 Aug 2024 14:09:44 +0200 Subject: [PATCH 043/187] Fix typos in Javadoc and assertion message. Closes #2965 --- .../springframework/data/redis/core/BoundListOperations.java | 2 +- .../org/springframework/data/redis/core/ListOperations.java | 2 +- .../springframework/data/redis/core/ReactiveListOperations.java | 2 +- .../springframework/data/redis/core/types/RedisClientInfo.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java index a33fab90d7..2e76833c39 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java @@ -231,7 +231,7 @@ public interface BoundListOperations extends BoundKeyOperations { V getLast(); /** - * Get element at {@code index} form list at the bound key. + * Get element at {@code index} from list at the bound key. * * @param index * @return {@literal null} when used in pipeline / transaction. diff --git a/src/main/java/org/springframework/data/redis/core/ListOperations.java b/src/main/java/org/springframework/data/redis/core/ListOperations.java index 5a946c62db..f453aba9a6 100644 --- a/src/main/java/org/springframework/data/redis/core/ListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ListOperations.java @@ -393,7 +393,7 @@ default V getLast(K key) { } /** - * Get element at {@code index} form list at {@code key}. + * Get element at {@code index} from list at {@code key}. * * @param key must not be {@literal null}. * @param index diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java index 73ff7efa8c..acaf2e4344 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java @@ -302,7 +302,7 @@ default Mono getLast(K key) { } /** - * Get element at {@code index} form list at {@code key}. + * Get element at {@code index} from list at {@code key}. * * @param key must not be {@literal null}. * @param index diff --git a/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java b/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java index 898c25b180..3db864730f 100644 --- a/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java +++ b/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java @@ -269,7 +269,7 @@ public static class RedisClientInfoBuilder { public static RedisClientInfo fromString(String source) { - Assert.notNull(source, "Cannot read client properties form 'null'"); + Assert.notNull(source, "Cannot read client properties from 'null'"); Properties properties = new Properties(); try { properties.load(new StringReader(source.replace(' ', '\n'))); From 486dc9762340ff20edf8b8dcd3eb8177008d8214 Mon Sep 17 00:00:00 2001 From: jinkshower Date: Sun, 7 Jul 2024 21:13:59 +0900 Subject: [PATCH 044/187] Add overloads for StreamOperations add with additional XAddOptions. Closes: #2915 Original Pull Request: #2936 --- .../connection/ReactiveStreamCommands.java | 33 +++- .../core/DefaultReactiveStreamOperations.java | 14 ++ .../redis/core/DefaultStreamOperations.java | 17 ++ .../redis/core/ReactiveStreamOperations.java | 59 +++++++ .../data/redis/core/StreamOperations.java | 49 ++++++ ...ctiveStreamOperationsIntegrationTests.java | 166 ++++++++++++++++++ ...faultStreamOperationsIntegrationTests.java | 151 ++++++++++++++++ 7 files changed, 488 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java index 460a8cc3fe..8ba39b08e0 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java @@ -32,6 +32,7 @@ import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand; import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.connection.RedisStreamCommands.XClaimOptions; +import org.springframework.data.redis.connection.RedisStreamCommands.XAddOptions; import org.springframework.data.redis.connection.RedisStreamCommands.XPendingOptions; import org.springframework.data.redis.connection.stream.ByteBufferRecord; import org.springframework.data.redis.connection.stream.Consumer; @@ -58,6 +59,7 @@ * @author Tugdual Grall * @author Dengliming * @author Mark John Moreno + * @author jinkshower * @since 2.2 */ public interface ReactiveStreamCommands { @@ -394,11 +396,40 @@ default Mono xAdd(ByteBufferRecord record) { return xAdd(Mono.just(AddStreamRecord.of(record))).next().map(CommandResponse::getOutput); } + /** + * Add stream record with the specified options. + * + * @param record must not be {@literal null}. + * @param xAddOptions parameters for the {@literal XADD} call. Must not be {@literal null}. + * @return {@link Mono} the {@link RecordId id}. + * @see Redis Documentation: XADD + * @since 3.3 + */ + default Mono xAdd(ByteBufferRecord record, XAddOptions xAddOptions) { + + Assert.notNull(record, "Record must not be null"); + Assert.notNull(xAddOptions, "XAddOptions must not be null"); + + AddStreamRecord addStreamRecord = AddStreamRecord.of(record) + .approximateTrimming(xAddOptions.isApproximateTrimming()) + .makeNoStream(xAddOptions.isNoMkStream()); + + if (xAddOptions.hasMaxlen()) { + addStreamRecord = addStreamRecord.maxlen(xAddOptions.getMaxlen()); + } + + if (xAddOptions.hasMinId()) { + addStreamRecord = addStreamRecord.minId(xAddOptions.getMinId()); + } + + return xAdd(Mono.just(addStreamRecord)).next().map(CommandResponse::getOutput); + } + /** * Add stream record with given {@literal body} to {@literal key}. * * @param commands must not be {@literal null}. - * @return {@link Flux} emitting the {@link RecordId} on by for for the given {@link AddStreamRecord} commands. + * @return {@link Flux} emitting the {@link RecordId} on by for the given {@link AddStreamRecord} commands. * @see Redis Documentation: XADD */ Flux> xAdd(Publisher commands); diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveStreamOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveStreamOperations.java index 432cf77283..a3d7b93668 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveStreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveStreamOperations.java @@ -33,6 +33,7 @@ import org.springframework.data.redis.connection.Limit; import org.springframework.data.redis.connection.ReactiveStreamCommands; import org.springframework.data.redis.connection.RedisStreamCommands.XClaimOptions; +import org.springframework.data.redis.connection.RedisStreamCommands.XAddOptions; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.connection.stream.ByteBufferRecord; import org.springframework.data.redis.connection.stream.Consumer; @@ -60,6 +61,7 @@ * @author Christoph Strobl * @author Marcin Zielinski * @author John Blum + * @author jinkshower * @since 2.2 */ class DefaultReactiveStreamOperations implements ReactiveStreamOperations { @@ -146,6 +148,18 @@ public Mono add(Record record) { return createMono(streamCommands -> streamCommands.xAdd(serializeRecord(input))); } + @Override + public Mono add(Record record, XAddOptions xAddOptions) { + + Assert.notNull(record.getStream(), "Key must not be null"); + Assert.notNull(record.getValue(), "Body must not be null"); + Assert.notNull(xAddOptions, "XAddOptions must not be null"); + + MapRecord input = StreamObjectMapper.toMapRecord(this, record); + + return createMono(streamCommands -> streamCommands.xAdd(serializeRecord(input), xAddOptions)); + } + @Override public Flux> claim(K key, String consumerGroup, String newOwner, XClaimOptions xClaimOptions) { diff --git a/src/main/java/org/springframework/data/redis/core/DefaultStreamOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultStreamOperations.java index 52eaa318ae..a53b18d030 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultStreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultStreamOperations.java @@ -26,6 +26,7 @@ import org.springframework.data.domain.Range; import org.springframework.data.redis.connection.Limit; import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisStreamCommands.XAddOptions; import org.springframework.data.redis.connection.RedisStreamCommands.XClaimOptions; import org.springframework.data.redis.connection.stream.ByteRecord; import org.springframework.data.redis.connection.stream.Consumer; @@ -54,6 +55,7 @@ * @author Christoph Strobl * @author Marcin Zielinski * @author John Blum + * @author jinkshower * @since 2.2 */ class DefaultStreamOperations extends AbstractOperations implements StreamOperations { @@ -136,6 +138,21 @@ public RecordId add(Record record) { return execute(connection -> connection.xAdd(binaryRecord)); } + @Nullable + @Override + @SuppressWarnings("unchecked") + public RecordId add(Record record, XAddOptions options) { + + Assert.notNull(record, "Record must not be null"); + Assert.notNull(options, "XAddOptions must not be null"); + + MapRecord input = StreamObjectMapper.toMapRecord(this, record); + + ByteRecord binaryRecord = input.serialize(keySerializer(), hashKeySerializer(), hashValueSerializer()); + + return execute(connection -> connection.streamCommands().xAdd(binaryRecord, options)); + } + @Override public List> claim(K key, String consumerGroup, String newOwner, XClaimOptions xClaimOptions) { diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java index fc3ca29651..e4b905030e 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java @@ -26,6 +26,7 @@ import org.springframework.data.domain.Range; import org.springframework.data.redis.connection.Limit; import org.springframework.data.redis.connection.RedisStreamCommands.XClaimOptions; +import org.springframework.data.redis.connection.RedisStreamCommands.XAddOptions; import org.springframework.data.redis.connection.stream.ByteBufferRecord; import org.springframework.data.redis.connection.stream.Consumer; import org.springframework.data.redis.connection.stream.MapRecord; @@ -54,6 +55,7 @@ * @author Dengliming * @author Marcin Zielinski * @author John Blum + * @author jinkshower * @since 2.2 */ public interface ReactiveStreamOperations extends HashMapperProvider { @@ -94,6 +96,63 @@ default Mono acknowledge(String group, Record record) { return acknowledge(record.getRequiredStream(), group, record.getId()); } + /** + * Append one or more records to the stream {@code key} with the specified options. + * + * @param key the stream key. + * @param bodyPublisher record body {@link Publisher}. + * @param xAddOptions parameters for the {@literal XADD} call. + * @return the record Ids. + * @see Redis Documentation: XADD + * @since 3.3 + */ + default Flux add (K key, Publisher> bodyPublisher, + XAddOptions xAddOptions) { + return Flux.from(bodyPublisher).flatMap(it -> add(key, it, xAddOptions)); + } + + /** + * Append a record to the stream {@code key} with the specified options. + * + * @param key the stream key. + * @param content record content as Map. + * @param xAddOptions parameters for the {@literal XADD} call. + * @return the {@link Mono} emitting the {@link RecordId}. + * @see Redis Documentation: XADD + * @since 3.3 + */ + default Mono add(K key, Map content, XAddOptions xAddOptions) { + return add(StreamRecords.newRecord().in(key).ofMap(content), xAddOptions); + } + + /** + * Append a record, backed by a {@link Map} holding the field/value pairs, to the stream with the specified options. + * + * @param record the record to append. + * @param xAddOptions parameters for the {@literal XADD} call. + * @return the {@link Mono} emitting the {@link RecordId}. + * @see Redis Documentation: XADD + * @since 3.3 + */ + @SuppressWarnings("unchecked") + default Mono add(MapRecord record, XAddOptions xAddOptions) { + return add((Record) record, xAddOptions); + } + + /** + * Append the record, backed by the given value, to the stream with the specified options. + * The value will be hashed and serialized. + * + * @param record must not be {@literal null}. + * @param xAddOptions parameters for the {@literal XADD} call. Must not be {@literal null}. + * @return the {@link Mono} emitting the {@link RecordId}. + * @see MapRecord + * @see ObjectRecord + * @see Redis Documentation: XADD + * @since 3.3 + */ + Mono add(Record record, XAddOptions xAddOptions); + /** * Append one or more records to the stream {@code key}. * diff --git a/src/main/java/org/springframework/data/redis/core/StreamOperations.java b/src/main/java/org/springframework/data/redis/core/StreamOperations.java index 4636c5bcf6..c38a524ff0 100644 --- a/src/main/java/org/springframework/data/redis/core/StreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/StreamOperations.java @@ -25,6 +25,7 @@ import org.springframework.data.domain.Range; import org.springframework.data.redis.connection.Limit; import org.springframework.data.redis.connection.RedisStreamCommands.XClaimOptions; +import org.springframework.data.redis.connection.RedisStreamCommands.XAddOptions; import org.springframework.data.redis.connection.stream.ByteRecord; import org.springframework.data.redis.connection.stream.Consumer; import org.springframework.data.redis.connection.stream.MapRecord; @@ -53,6 +54,7 @@ * @author Dengliming * @author Marcin Zielinski * @author John Blum + * @author jinkshower * @since 2.2 */ public interface StreamOperations extends HashMapperProvider { @@ -95,6 +97,53 @@ default Long acknowledge(String group, Record record) { return acknowledge(record.getRequiredStream(), group, record.getId()); } + /** + * Append a record to the stream {@code key} with the specified options. + * + * @param key the stream key. + * @param content record content as Map. + * @param xAddOptions additional parameters for the {@literal XADD} call. + * @return the record Id. {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: XADD + * @since 3.3 + */ + @SuppressWarnings("unchecked") + @Nullable + default RecordId add(K key, Map content, XAddOptions xAddOptions) { + return add(StreamRecords.newRecord().in(key).ofMap(content), xAddOptions); + } + + /** + * Append a record, backed by a {@link Map} holding the field/value pairs, to the stream with the specified options. + * + * @param record the record to append. + * @param xAddOptions additional parameters for the {@literal XADD} call. + * @return the record Id. {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: XADD + * @since 3.3 + */ + @SuppressWarnings("unchecked") + @Nullable + default RecordId add(MapRecord record, XAddOptions xAddOptions) { + return add((Record) record, xAddOptions); + } + + /** + * Append the record, backed by the given value, to the stream with the specified options. + * The value will be hashed and serialized. + * + * @param record must not be {@literal null}. + * @param xAddOptions parameters for the {@literal XADD} call. Must not be {@literal null}. + * @return the record Id. {@literal null} when used in pipeline / transaction. + * @see MapRecord + * @see ObjectRecord + * @see Redis Documentation: XADD + * @since 3.3 + */ + @SuppressWarnings("unchecked") + @Nullable + RecordId add(Record record, XAddOptions xAddOptions); + /** * Append a record to the stream {@code key}. * diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java index c6040d795c..27cf52f0f7 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java @@ -35,6 +35,7 @@ import org.springframework.data.redis.connection.Limit; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStreamCommands.XAddOptions; import org.springframework.data.redis.connection.stream.Consumer; import org.springframework.data.redis.connection.stream.MapRecord; import org.springframework.data.redis.connection.stream.ReadOffset; @@ -61,6 +62,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Marcin Zielinski + * @author jinkshower */ @MethodSource("testParams") @SuppressWarnings("unchecked") @@ -192,6 +194,170 @@ void addShouldAddReadSimpleMessageWithRawSerializer() { .verifyComplete(); } + @ParameterizedRedisTest // GH-2915 + void addMaxLenShouldLimitMessagesSize() { + + K key = keyFactory.instance(); + HK hashKey = hashKeyFactory.instance(); + HV value = valueFactory.instance(); + + streamOperations.add(key, Collections.singletonMap(hashKey, value)).block(); + + HV newValue = valueFactory.instance(); + XAddOptions options = XAddOptions.maxlen(1).approximateTrimming(false); + + RecordId messageId = streamOperations.add(key, Collections.singletonMap(hashKey, newValue), options).block(); + + streamOperations.range(key, Range.unbounded()).as(StepVerifier::create) + .consumeNextWith(actual -> { + + assertThat(actual.getId()).isEqualTo(messageId); + assertThat(actual.getStream()).isEqualTo(key); + assertThat(actual).hasSize(1); + + if (!(key instanceof byte[] || value instanceof byte[])) { + assertThat(actual.getValue()).containsEntry(hashKey, newValue); + } + + }) + .verifyComplete(); + } + + @ParameterizedRedisTest // GH-2915 + void addMaxLenShouldLimitSimpleMessagesSize() { + + assumeTrue(!(serializer instanceof Jackson2JsonRedisSerializer) + && !(serializer instanceof GenericJackson2JsonRedisSerializer) + && !(serializer instanceof JdkSerializationRedisSerializer) && !(serializer instanceof OxmSerializer)); + + K key = keyFactory.instance(); + HV value = valueFactory.instance(); + + streamOperations.add(StreamRecords.objectBacked(value).withStreamKey(key)).block(); + + HV newValue = valueFactory.instance(); + XAddOptions options = XAddOptions.maxlen(1).approximateTrimming(false); + + RecordId messageId = streamOperations.add(StreamRecords.objectBacked(newValue).withStreamKey(key), options).block(); + + streamOperations.range((Class) value.getClass(), key, Range.unbounded()).as(StepVerifier::create) + .consumeNextWith(actual -> { + + assertThat(actual.getId()).isEqualTo(messageId); + assertThat(actual.getStream()).isEqualTo(key); + assertThat(actual.getValue()).isEqualTo(newValue); + + }) + .expectNextCount(0) + .verifyComplete(); + } + + @ParameterizedRedisTest // GH-2915 + void addMaxLenShouldLimitSimpleMessageWithRawSerializerSize() { + + assumeTrue(!(serializer instanceof Jackson2JsonRedisSerializer) + && !(serializer instanceof GenericJackson2JsonRedisSerializer)); + + SerializationPair keySerializer = redisTemplate.getSerializationContext().getKeySerializationPair(); + + RedisSerializationContext serializationContext = RedisSerializationContext + . newSerializationContext(StringRedisSerializer.UTF_8).key(keySerializer) + .hashValue(SerializationPair.raw()).hashKey(SerializationPair.raw()).build(); + + ReactiveRedisTemplate raw = new ReactiveRedisTemplate<>(redisTemplate.getConnectionFactory(), + serializationContext); + + K key = keyFactory.instance(); + Person value = new PersonObjectFactory().instance(); + + raw.opsForStream().add(StreamRecords.objectBacked(value).withStreamKey(key)).block(); + + Person newValue = new PersonObjectFactory().instance(); + XAddOptions options = XAddOptions.maxlen(1).approximateTrimming(false); + + RecordId messageId = raw.opsForStream().add(StreamRecords.objectBacked(newValue).withStreamKey(key), options).block(); + + raw.opsForStream().range((Class) value.getClass(), key, Range.unbounded()).as(StepVerifier::create) + .consumeNextWith(it -> { + + assertThat(it.getId()).isEqualTo(messageId); + assertThat(it.getStream()).isEqualTo(key); + assertThat(it.getValue()).isEqualTo(newValue); + + }) + .expectNextCount(0) + .verifyComplete(); + } + + @ParameterizedRedisTest // GH-2915 + void addMinIdShouldEvictLowerIdMessages() { + + K key = keyFactory.instance(); + HK hashKey = hashKeyFactory.instance(); + HV value = valueFactory.instance(); + + streamOperations.add(key, Collections.singletonMap(hashKey, value)).block(); + RecordId messageId1 = streamOperations.add(key, Collections.singletonMap(hashKey, value)).block(); + + XAddOptions options = XAddOptions.none().minId(messageId1); + + RecordId messageId2 = streamOperations.add(key, Collections.singletonMap(hashKey, value), options).block(); + + streamOperations.range(key, Range.unbounded()).as(StepVerifier::create) + .consumeNextWith(actual -> { + assertThat(actual.getId()).isEqualTo(messageId1); + assertThat(actual.getStream()).isEqualTo(key); + }) + .consumeNextWith(actual -> { + assertThat(actual.getId()).isEqualTo(messageId2); + assertThat(actual.getStream()).isEqualTo(key); + }) + .expectNextCount(0) + .verifyComplete(); + } + + @ParameterizedRedisTest // GH-2915 + void addMakeNoStreamShouldNotCreateStreamWhenNoStreamExists() { + + K key = keyFactory.instance(); + HK hashKey = hashKeyFactory.instance(); + HV value = valueFactory.instance(); + + XAddOptions options = XAddOptions.makeNoStream(); + + streamOperations.add(key, Collections.singletonMap(hashKey, value), options).block(); + + streamOperations.size(key).as(StepVerifier::create) + .expectNext(0L) + .verifyComplete(); + + streamOperations.range(key, Range.unbounded()).as(StepVerifier::create) + .expectNextCount(0L) + .verifyComplete(); + } + + @ParameterizedRedisTest // GH-2915 + void addMakeNoStreamShouldCreateStreamWhenStreamExists() { + + K key = keyFactory.instance(); + HK hashKey = hashKeyFactory.instance(); + HV value = valueFactory.instance(); + + streamOperations.add(key, Collections.singletonMap(hashKey, value)).block(); + + XAddOptions options = XAddOptions.makeNoStream(); + + streamOperations.add(key, Collections.singletonMap(hashKey, value), options).block(); + + streamOperations.size(key).as(StepVerifier::create) + .expectNext(2L) + .verifyComplete(); + + streamOperations.range(key, Range.unbounded()).as(StepVerifier::create) + .expectNextCount(2L) + .verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-864 void rangeShouldReportMessages() { diff --git a/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java index eb2cb33079..06ab8c5982 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java @@ -32,6 +32,7 @@ import org.springframework.data.redis.Person; import org.springframework.data.redis.connection.Limit; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStreamCommands.XAddOptions; import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.connection.lettuce.extension.LettuceConnectionFactoryExtension; @@ -51,6 +52,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Marcin Zielinski + * @author jinkshower */ @MethodSource("testParams") @EnabledOnCommand("XADD") @@ -149,6 +151,155 @@ void addShouldAddReadSimpleMessage() { assertThat(message.getValue()).isEqualTo(value); } + @ParameterizedRedisTest // GH-2915 + void addMaxLenShouldLimitMessagesSize() { + + K key = keyFactory.instance(); + HK hashKey = hashKeyFactory.instance(); + HV value = hashValueFactory.instance(); + + streamOps.add(key, Collections.singletonMap(hashKey, value)); + + HV newValue = hashValueFactory.instance(); + + XAddOptions options = XAddOptions.maxlen(1).approximateTrimming(false); + + RecordId messageId = streamOps.add(key, Collections.singletonMap(hashKey, newValue), options); + + List> messages = streamOps.range(key, Range.unbounded()); + + assertThat(messages).hasSize(1); + + MapRecord message = messages.get(0); + + assertThat(message.getId()).isEqualTo(messageId); + assertThat(message.getStream()).isEqualTo(key); + + if (!(key instanceof byte[] || value instanceof byte[])) { + assertThat(message.getValue()).containsEntry(hashKey, newValue); + } + } + + @ParameterizedRedisTest // GH-2915 + void addMaxLenShouldLimitSimpleMessagesSize() { + + K key = keyFactory.instance(); + HV value = hashValueFactory.instance(); + + streamOps.add(StreamRecords.objectBacked(value).withStreamKey(key)); + + HV newValue = hashValueFactory.instance(); + + XAddOptions options = XAddOptions.maxlen(1).approximateTrimming(false); + + RecordId messageId = streamOps.add(StreamRecords.objectBacked(newValue).withStreamKey(key), options); + + List> messages = streamOps.range((Class) value.getClass(), key, Range.unbounded()); + + assertThat(messages).hasSize(1); + + ObjectRecord message = messages.get(0); + + assertThat(message.getId()).isEqualTo(messageId); + assertThat(message.getStream()).isEqualTo(key); + + assertThat(message.getValue()).isEqualTo(newValue); + } + + @ParameterizedRedisTest // GH-2915 + void addMinIdShouldEvictLowerIdMessages() { + + K key = keyFactory.instance(); + HK hashKey = hashKeyFactory.instance(); + HV value = hashValueFactory.instance(); + + streamOps.add(key, Collections.singletonMap(hashKey, value)); + RecordId messageId1 = streamOps.add(key, Collections.singletonMap(hashKey, value)); + + XAddOptions options = XAddOptions.none().minId(messageId1); + + RecordId messageId2 = streamOps.add(key, Collections.singletonMap(hashKey, value), options); + + List> messages = streamOps.range(key, Range.unbounded()); + + assertThat(messages).hasSize(2); + + MapRecord message1 = messages.get(0); + + assertThat(message1.getId()).isEqualTo(messageId1); + assertThat(message1.getStream()).isEqualTo(key); + + MapRecord message2 = messages.get(1); + + assertThat(message2.getId()).isEqualTo(messageId2); + assertThat(message2.getStream()).isEqualTo(key); + + if (!(key instanceof byte[] || value instanceof byte[])) { + assertThat(message1.getValue()).containsEntry(hashKey, value); + assertThat(message2.getValue()).containsEntry(hashKey, value); + } + } + + @ParameterizedRedisTest // GH-2915 + void addMinIdShouldEvictLowerIdSimpleMessages() { + + K key = keyFactory.instance(); + HV value = hashValueFactory.instance(); + + streamOps.add(StreamRecords.objectBacked(value).withStreamKey(key)); + RecordId messageId1 = streamOps.add(StreamRecords.objectBacked(value).withStreamKey(key)); + + XAddOptions options = XAddOptions.none().minId(messageId1); + + RecordId messageId2 = streamOps.add(StreamRecords.objectBacked(value).withStreamKey(key), options); + + List> messages = streamOps.range((Class) value.getClass(), key, Range.unbounded()); + + assertThat(messages).hasSize(2); + + ObjectRecord message1 = messages.get(0); + + assertThat(message1.getId()).isEqualTo(messageId1); + assertThat(message1.getStream()).isEqualTo(key); + assertThat(message1.getValue()).isEqualTo(value); + + ObjectRecord message2 = messages.get(1); + + assertThat(message2.getId()).isEqualTo(messageId2); + assertThat(message2.getStream()).isEqualTo(key); + assertThat(message2.getValue()).isEqualTo(value); + } + + @ParameterizedRedisTest // GH-2915 + void addMakeNoStreamShouldNotCreateStreamWhenNoStreamExists() { + + K key = keyFactory.instance(); + HV value = hashValueFactory.instance(); + + XAddOptions options = XAddOptions.makeNoStream(); + + streamOps.add(StreamRecords.objectBacked(value).withStreamKey(key), options); + + assertThat(streamOps.size(key)).isZero(); + assertThat(streamOps.range(key, Range.unbounded())).isEmpty(); + } + + @ParameterizedRedisTest // GH-2915 + void addMakeNoStreamShouldCreateStreamWhenStreamExists() { + + K key = keyFactory.instance(); + HV value = hashValueFactory.instance(); + + streamOps.add(StreamRecords.objectBacked(value).withStreamKey(key)); + + XAddOptions options = XAddOptions.makeNoStream(); + + streamOps.add(StreamRecords.objectBacked(value).withStreamKey(key), options); + + assertThat(streamOps.size(key)).isEqualTo(2); + assertThat(streamOps.range(key, Range.unbounded())).hasSize(2); + } + @ParameterizedRedisTest // DATAREDIS-864 void simpleMessageReadWriteSymmetry() { From fb0f0bc07cd8b9edcc97ed1fe0a466ba0d577143 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 3 Sep 2024 07:23:29 +0200 Subject: [PATCH 045/187] Polishing. Add new method to BoundStreamOperations and remove add accepting a Publisher. Original Pull Request: #2936 --- .../connection/ReactiveStreamCommands.java | 2 +- .../redis/core/BoundStreamOperations.java | 13 ++++++++++++ .../redis/core/ReactiveStreamOperations.java | 21 +++---------------- .../data/redis/core/StreamOperations.java | 6 +++--- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java index 8ba39b08e0..f6e5687cf9 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java @@ -403,7 +403,7 @@ default Mono xAdd(ByteBufferRecord record) { * @param xAddOptions parameters for the {@literal XADD} call. Must not be {@literal null}. * @return {@link Mono} the {@link RecordId id}. * @see Redis Documentation: XADD - * @since 3.3 + * @since 3.4 */ default Mono xAdd(ByteBufferRecord record, XAddOptions xAddOptions) { diff --git a/src/main/java/org/springframework/data/redis/core/BoundStreamOperations.java b/src/main/java/org/springframework/data/redis/core/BoundStreamOperations.java index b589c99163..bbba7bf013 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundStreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundStreamOperations.java @@ -20,6 +20,7 @@ import org.springframework.data.domain.Range; import org.springframework.data.redis.connection.Limit; +import org.springframework.data.redis.connection.RedisStreamCommands.XAddOptions; import org.springframework.data.redis.connection.stream.Consumer; import org.springframework.data.redis.connection.stream.MapRecord; import org.springframework.data.redis.connection.stream.ReadOffset; @@ -58,6 +59,18 @@ public interface BoundStreamOperations { @Nullable RecordId add(Map body); + /** + * Append a record to the stream {@code key} with the specified options. + * + * @param content record content as Map. + * @param xAddOptions additional parameters for the {@literal XADD} call. + * @return the record Id. {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: XADD + * @since 3.4 + */ + @Nullable + RecordId add(Map content, XAddOptions xAddOptions); + /** * Removes the specified entries from the stream. Returns the number of items deleted, that may be different from the * number of IDs passed in case certain IDs do not exist. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java index e4b905030e..65697faf2a 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java @@ -96,21 +96,6 @@ default Mono acknowledge(String group, Record record) { return acknowledge(record.getRequiredStream(), group, record.getId()); } - /** - * Append one or more records to the stream {@code key} with the specified options. - * - * @param key the stream key. - * @param bodyPublisher record body {@link Publisher}. - * @param xAddOptions parameters for the {@literal XADD} call. - * @return the record Ids. - * @see Redis Documentation: XADD - * @since 3.3 - */ - default Flux add (K key, Publisher> bodyPublisher, - XAddOptions xAddOptions) { - return Flux.from(bodyPublisher).flatMap(it -> add(key, it, xAddOptions)); - } - /** * Append a record to the stream {@code key} with the specified options. * @@ -119,7 +104,7 @@ default Flux add (K key, PublisherRedis Documentation: XADD - * @since 3.3 + * @since 3.4 */ default Mono add(K key, Map content, XAddOptions xAddOptions) { return add(StreamRecords.newRecord().in(key).ofMap(content), xAddOptions); @@ -132,7 +117,7 @@ default Mono add(K key, Map content, XAddO * @param xAddOptions parameters for the {@literal XADD} call. * @return the {@link Mono} emitting the {@link RecordId}. * @see Redis Documentation: XADD - * @since 3.3 + * @since 3.4 */ @SuppressWarnings("unchecked") default Mono add(MapRecord record, XAddOptions xAddOptions) { @@ -149,7 +134,7 @@ default Mono add(MapRecord record, XAdd * @see MapRecord * @see ObjectRecord * @see Redis Documentation: XADD - * @since 3.3 + * @since 3.4 */ Mono add(Record record, XAddOptions xAddOptions); diff --git a/src/main/java/org/springframework/data/redis/core/StreamOperations.java b/src/main/java/org/springframework/data/redis/core/StreamOperations.java index c38a524ff0..fa30db9f7d 100644 --- a/src/main/java/org/springframework/data/redis/core/StreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/StreamOperations.java @@ -105,7 +105,7 @@ default Long acknowledge(String group, Record record) { * @param xAddOptions additional parameters for the {@literal XADD} call. * @return the record Id. {@literal null} when used in pipeline / transaction. * @see Redis Documentation: XADD - * @since 3.3 + * @since 3.4 */ @SuppressWarnings("unchecked") @Nullable @@ -120,7 +120,7 @@ default RecordId add(K key, Map content, XAddOptions * @param xAddOptions additional parameters for the {@literal XADD} call. * @return the record Id. {@literal null} when used in pipeline / transaction. * @see Redis Documentation: XADD - * @since 3.3 + * @since 3.4 */ @SuppressWarnings("unchecked") @Nullable @@ -138,7 +138,7 @@ default RecordId add(MapRecord record, XAddOption * @see MapRecord * @see ObjectRecord * @see Redis Documentation: XADD - * @since 3.3 + * @since 3.4 */ @SuppressWarnings("unchecked") @Nullable From 777f0791143c5c42975760e5e027925c7b3f050c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 4 Sep 2024 10:41:40 +0200 Subject: [PATCH 046/187] Deprecate Micrometer Tracing code in favor of Lettuce's Micrometer support. Closes #2561 --- .../DefaultLettuceObservationConvention.java | 13 ++++++++----- .../observability/LettuceObservationContext.java | 3 +++ .../observability/LettuceObservationConvention.java | 3 +++ .../observability/MicrometerTracingAdapter.java | 3 +++ .../lettuce/observability/RedisObservation.java | 3 +++ .../observability/SocketAddressEndpoint.java | 7 +++++-- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java index fa662181f5..7d400eb9ba 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java @@ -15,22 +15,25 @@ */ package org.springframework.data.redis.connection.lettuce.observability; +import io.lettuce.core.protocol.RedisCommand; +import io.lettuce.core.tracing.Tracing.Endpoint; +import io.micrometer.common.KeyValues; + import java.net.InetSocketAddress; import java.util.Locale; import org.springframework.data.redis.connection.lettuce.observability.RedisObservation.HighCardinalityCommandKeyNames; import org.springframework.data.redis.connection.lettuce.observability.RedisObservation.LowCardinalityCommandKeyNames; -import io.lettuce.core.protocol.RedisCommand; -import io.lettuce.core.tracing.Tracing.Endpoint; -import io.micrometer.common.KeyValues; - /** * Default {@link LettuceObservationConvention} implementation. * * @author Mark Paluch * @since 3.0 + * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through + * {@link io.lettuce.core.tracing.MicrometerTracing}. */ +@Deprecated(since = "3.4", forRemoval = true) record DefaultLettuceObservationConvention( boolean includeCommandArgsInSpanTags) implements LettuceObservationConvention { @@ -43,7 +46,7 @@ public KeyValues getLowCardinalityKeyValues(LettuceObservationContext context) { if (ep instanceof SocketAddressEndpoint endpoint) { - if (endpoint.socketAddress()instanceof InetSocketAddress inet) { + if (endpoint.socketAddress() instanceof InetSocketAddress inet) { keyValues = keyValues .and(KeyValues.of(LowCardinalityCommandKeyNames.NET_SOCK_PEER_ADDR.withValue(inet.getHostString()), LowCardinalityCommandKeyNames.NET_SOCK_PEER_PORT.withValue("" + inet.getPort()), diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java index e1a77d3e7b..0acb956297 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java @@ -28,7 +28,10 @@ * * @author Mark Paluch * @since 3.0 + * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through + * {@link io.lettuce.core.tracing.MicrometerTracing}. */ +@Deprecated(since = "3.4", forRemoval = true) public class LettuceObservationContext extends SenderContext { private volatile @Nullable RedisCommand command; diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java index 92c1e0ee0a..55a9316c14 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java @@ -23,7 +23,10 @@ * * @author Mark Paluch * @since 3.0 + * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through + * {@link io.lettuce.core.tracing.MicrometerTracing}. */ +@Deprecated(since = "3.4", forRemoval = true) interface LettuceObservationConvention extends ObservationConvention { @Override diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java index 010c5eb155..21c28a41ee 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java @@ -46,7 +46,10 @@ * @author Mark Paluch * @author Yanming Zhou * @since 3.0 + * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through + * {@link io.lettuce.core.tracing.MicrometerTracing}. */ +@Deprecated(since = "3.4", forRemoval = true) public class MicrometerTracingAdapter implements Tracing { private static final Log log = LogFactory.getLog(MicrometerTracingAdapter.class); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java index fb57b0fd82..6654be9bd1 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java @@ -23,7 +23,10 @@ * * @author Mark Paluch * @since 3.0 + * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through + * {@link io.lettuce.core.tracing.MicrometerTracing}. */ +@Deprecated(since = "3.4", forRemoval = true) public enum RedisObservation implements ObservationDocumentation { /** diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java index ecc6b26fe1..f2c311266d 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java @@ -15,14 +15,17 @@ */ package org.springframework.data.redis.connection.lettuce.observability; +import io.lettuce.core.tracing.Tracing.Endpoint; + import java.net.InetSocketAddress; import java.net.SocketAddress; -import io.lettuce.core.tracing.Tracing.Endpoint; - /** * @author Mark Paluch + * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through + * {@link io.lettuce.core.tracing.MicrometerTracing}. */ +@Deprecated(since = "3.4", forRemoval = true) record SocketAddressEndpoint(SocketAddress socketAddress) implements Endpoint { @Override From 06bbd3dcab07ba251c20226a38200a7ae834d98d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 29 Jul 2024 09:23:37 +0200 Subject: [PATCH 047/187] Use Redis locking for value retrieval synchronization. We now use RedisCacheWriter to acquire and maintain the lock for value retrieval synchronization. Closes: #2890 Original Pull Request: #2948 --- .../redis/cache/DefaultRedisCacheWriter.java | 76 ++++++++-- .../data/redis/cache/RedisCache.java | 36 ++--- .../data/redis/cache/RedisCacheWriter.java | 68 ++++++--- .../cache/DefaultRedisCacheWriterTests.java | 16 +++ .../data/redis/cache/RedisCacheTests.java | 133 +++++------------- 5 files changed, 169 insertions(+), 160 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java index 205487fa1b..e7fe73ab69 100644 --- a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java @@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; +import java.util.function.Supplier; import org.springframework.dao.PessimisticLockingFailureException; import org.springframework.data.redis.connection.ReactiveRedisConnection; @@ -137,9 +138,14 @@ public byte[] get(String name, byte[] key, @Nullable Duration ttl) { Assert.notNull(name, "Name must not be null"); Assert.notNull(key, "Key must not be null"); - byte[] result = shouldExpireWithin(ttl) - ? execute(name, connection -> connection.stringCommands().getEx(key, Expiration.from(ttl))) - : execute(name, connection -> connection.stringCommands().get(key)); + return execute(name, connection -> doGet(connection, name, key, ttl)); + } + + @Nullable + private byte[] doGet(RedisConnection connection, String name, byte[] key, @Nullable Duration ttl) { + + byte[] result = shouldExpireWithin(ttl) ? connection.stringCommands().getEx(key, Expiration.from(ttl)) + : connection.stringCommands().get(key); statistics.incGets(name); @@ -152,6 +158,50 @@ public byte[] get(String name, byte[] key, @Nullable Duration ttl) { return result; } + @Override + public byte[] get(String name, byte[] key, Supplier valueLoader, @Nullable Duration ttl, + boolean timeToIdleEnabled) { + + Assert.notNull(name, "Name must not be null"); + Assert.notNull(key, "Key must not be null"); + + boolean withTtl = shouldExpireWithin(ttl); + + // double-checked locking optimization + if (isLockingCacheWriter()) { + byte[] bytes = get(name, key, timeToIdleEnabled && withTtl ? ttl : null); + if (bytes != null) { + return bytes; + } + } + + return execute(name, connection -> { + + boolean wasLocked = false; + if (isLockingCacheWriter()) { + doLock(name, key, null, connection); + wasLocked = true; + } + + try { + + byte[] result = doGet(connection, name, key, timeToIdleEnabled && withTtl ? ttl : null); + + if (result != null) { + return result; + } + + byte[] value = valueLoader.get(); + doPut(connection, name, key, value, ttl); + return value; + } finally { + if (isLockingCacheWriter() && wasLocked) { + doUnlock(name, connection); + } + } + }); + } + @Override public boolean supportsAsyncRetrieve() { return asyncCacheWriter.isSupported(); @@ -186,17 +236,21 @@ public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) { Assert.notNull(value, "Value must not be null"); execute(name, connection -> { - - if (shouldExpireWithin(ttl)) { - connection.stringCommands().set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), - SetOption.upsert()); - } else { - connection.stringCommands().set(key, value); - } - + doPut(connection, name, key, value, ttl); return "OK"; }); + } + + private void doPut(RedisConnection connection, String name, byte[] key, byte[] value, @Nullable Duration ttl) { + + if (shouldExpireWithin(ttl)) { + connection.stringCommands().set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), + SetOption.upsert()); + } else { + connection.stringCommands().set(key, value); + } + statistics.incPuts(name); } diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCache.java b/src/main/java/org/springframework/data/redis/cache/RedisCache.java index 187add2512..fb94eb244e 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCache.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCache.java @@ -25,8 +25,6 @@ import java.util.StringJoiner; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import org.springframework.cache.Cache; @@ -64,8 +62,6 @@ public class RedisCache extends AbstractValueAdaptingCache { static final String CACHE_RETRIEVAL_UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE = "The Redis driver configured with RedisCache through RedisCacheWriter does not support CompletableFuture-based retrieval"; - private final Lock lock = new ReentrantLock(); - private final RedisCacheConfiguration cacheConfiguration; private final RedisCacheWriter cacheWriter; @@ -154,28 +150,18 @@ public CacheStatistics getStatistics() { @SuppressWarnings("unchecked") public T get(Object key, Callable valueLoader) { - ValueWrapper result = get(key); - - return result != null ? (T) result.get() : getSynchronized(key, valueLoader); - } - - @Nullable - @SuppressWarnings("unchecked") - private T getSynchronized(Object key, Callable valueLoader) { + byte[] binaryKey = createAndConvertCacheKey(key); + byte[] binaryValue = getCacheWriter().get(getName(), binaryKey, + () -> serializeCacheValue(toStoreValue(loadCacheValue(key, valueLoader))), getTimeToLive(key), + getCacheConfiguration().isTimeToIdleEnabled()); - lock.lock(); + ValueWrapper result = toValueWrapper(deserializeCacheValue(binaryValue)); - try { - ValueWrapper result = get(key); - return result != null ? (T) result.get() : loadCacheValue(key, valueLoader); - } finally { - lock.unlock(); - } + return result != null ? (T) result.get() : null; } /** - * Loads the {@link Object} using the given {@link Callable valueLoader} and {@link #put(Object, Object) puts} the - * {@link Object loaded value} in the cache. + * Loads the {@link Object} using the given {@link Callable valueLoader}. * * @param {@link Class type} of the loaded {@link Object cache value}. * @param key {@link Object key} mapped to the loaded {@link Object cache value}. @@ -184,17 +170,11 @@ private T getSynchronized(Object key, Callable valueLoader) { */ protected T loadCacheValue(Object key, Callable valueLoader) { - T value; - try { - value = valueLoader.call(); + return valueLoader.call(); } catch (Exception ex) { throw new ValueRetrievalException(key, valueLoader, ex); } - - put(key, value); - - return value; } @Override diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java index 04dd0e3507..fbf7e96dcf 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java @@ -24,14 +24,14 @@ import org.springframework.util.Assert; /** - * {@link RedisCacheWriter} provides low-level access to Redis commands ({@code SET, SETNX, GET, EXPIRE,...}) - * used for caching. + * {@link RedisCacheWriter} provides low-level access to Redis commands ({@code SET, SETNX, GET, EXPIRE,...}) used for + * caching. *

* The {@link RedisCacheWriter} may be shared by multiple cache implementations and is responsible for reading/writing * binary data from/to Redis. The implementation honors potential cache lock flags that might be set. *

- * The default {@link RedisCacheWriter} implementation can be customized with {@link BatchStrategy} - * to tune performance behavior. + * The default {@link RedisCacheWriter} implementation can be customized with {@link BatchStrategy} to tune performance + * behavior. * * @author Christoph Strobl * @author Mark Paluch @@ -96,9 +96,8 @@ static RedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectio * * @param connectionFactory must not be {@literal null}. * @param sleepTime sleep time between lock access attempts, must not be {@literal null}. - * @param lockTtlFunction TTL function to compute the Lock TTL. The function is called with contextual keys - * and values (such as the cache name on cleanup or the actual key/value on put requests); - * must not be {@literal null}. + * @param lockTtlFunction TTL function to compute the Lock TTL. The function is called with contextual keys and values + * (such as the cache name on cleanup or the actual key/value on put requests); must not be {@literal null}. * @param batchStrategy must not be {@literal null}. * @return new instance of {@link DefaultRedisCacheWriter}. * @since 3.2 @@ -124,8 +123,8 @@ static RedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectio byte[] get(String name, byte[] key); /** - * Get the binary value representation from Redis stored for the given key and set - * the given {@link Duration TTL expiration} for the cache entry. + * Get the binary value representation from Redis stored for the given key and set the given {@link Duration TTL + * expiration} for the cache entry. * * @param name must not be {@literal null}. * @param key must not be {@literal null}. @@ -138,14 +137,41 @@ default byte[] get(String name, byte[] key, @Nullable Duration ttl) { } /** - * Determines whether the asynchronous {@link #retrieve(String, byte[])} - * and {@link #retrieve(String, byte[], Duration)} cache operations are supported by the implementation. + * Get the binary value representation from Redis stored for the given key and set the given {@link Duration TTL + * expiration} for the cache entry, obtaining the value from {@code valueLoader} if necessary. *

- * The main factor for whether the {@literal retrieve} operation can be supported will primarily be determined - * by the Redis driver in use at runtime. + * If possible (and configured for locking), implementations should ensure that the loading operation is synchronized + * so that the specified {@code valueLoader} is only called once in case of concurrent access on the same key. + * + * @param name must not be {@literal null}. + * @param key must not be {@literal null}. + * @param valueLoader value loader that creates the value if the cache lookup has been not successful. + * @param ttl {@link Duration} specifying the {@literal expiration timeout} for the cache entry. + * @param timeToIdleEnabled {@literal true} to enable Time to Idle when retrieving the value. + * @since 3.4 + */ + default byte[] get(String name, byte[] key, Supplier valueLoader, @Nullable Duration ttl, + boolean timeToIdleEnabled) { + + byte[] bytes = timeToIdleEnabled ? get(name, key, ttl) : get(name, key); + + if (bytes == null) { + bytes = valueLoader.get(); + put(name, key, bytes, ttl); + } + + return bytes; + } + + /** + * Determines whether the asynchronous {@link #retrieve(String, byte[])} and + * {@link #retrieve(String, byte[], Duration)} cache operations are supported by the implementation. + *

+ * The main factor for whether the {@literal retrieve} operation can be supported will primarily be determined by the + * Redis driver in use at runtime. *

- * Returns {@literal false} by default. This will have an effect of {@link RedisCache#retrieve(Object)} - * and {@link RedisCache#retrieve(Object, Supplier)} throwing an {@link UnsupportedOperationException}. + * Returns {@literal false} by default. This will have an effect of {@link RedisCache#retrieve(Object)} and + * {@link RedisCache#retrieve(Object, Supplier)} throwing an {@link UnsupportedOperationException}. * * @return {@literal true} if asynchronous {@literal retrieve} operations are supported by the implementation. * @since 3.2 @@ -155,8 +181,8 @@ default boolean supportsAsyncRetrieve() { } /** - * Asynchronously retrieves the {@link CompletableFuture value} to which the {@link RedisCache} - * maps the given {@link byte[] key}. + * Asynchronously retrieves the {@link CompletableFuture value} to which the {@link RedisCache} maps the given + * {@link byte[] key}. *

* This operation is non-blocking. * @@ -171,8 +197,8 @@ default CompletableFuture retrieve(String name, byte[] key) { } /** - * Asynchronously retrieves the {@link CompletableFuture value} to which the {@link RedisCache} maps - * the given {@link byte[] key} setting the {@link Duration TTL expiration} for the cache entry. + * Asynchronously retrieves the {@link CompletableFuture value} to which the {@link RedisCache} maps the given + * {@link byte[] key} setting the {@link Duration TTL expiration} for the cache entry. *

* This operation is non-blocking. * @@ -264,8 +290,8 @@ interface TtlFunction { /** * Creates a {@literal Singleton} {@link TtlFunction} using the given {@link Duration}. * - * @param duration the time to live. Can be {@link Duration#ZERO} for persistent values (i.e. cache entry - * does not expire). + * @param duration the time to live. Can be {@link Duration#ZERO} for persistent values (i.e. cache entry does not + * expire). * @return a singleton {@link TtlFunction} using {@link Duration}. */ static TtlFunction just(Duration duration) { diff --git a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java index 1f07ea6110..e903b24bc6 100644 --- a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java +++ b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java @@ -242,6 +242,22 @@ void putIfAbsentShouldAddExpiringEntryWhenKeyDoesNotExist() { assertThat(writer.getCacheStatistics(CACHE_NAME).getPuts()).isOne(); } + @ParameterizedRedisTest // GH-2890 + void getWithValueLoaderShouldStoreCacheValue() { + + RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory) + .withStatisticsCollector(CacheStatisticsCollector.create()); + + writer.get(CACHE_NAME, binaryCacheKey, () -> binaryCacheValue, Duration.ofSeconds(5), true); + + doWithConnection(connection -> { + assertThat(connection.ttl(binaryCacheKey)).isGreaterThan(3).isLessThan(6); + }); + + assertThat(writer.getCacheStatistics(CACHE_NAME).getMisses()).isOne(); + assertThat(writer.getCacheStatistics(CACHE_NAME).getPuts()).isOne(); + } + @ParameterizedRedisTest // DATAREDIS-481, DATAREDIS-1082 void removeShouldDeleteEntry() { diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java index 6677ab7a9d..4dfbd0d1ec 100644 --- a/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java +++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java @@ -35,6 +35,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; @@ -251,6 +252,37 @@ void getShouldReturnValueWrapperHoldingNullIfNullValueStored() { assertThat(result.get()).isEqualTo(null); } + @ParameterizedRedisTest // GH-2890 + void getWithValueLoaderShouldStoreNull() { + + doWithConnection(connection -> connection.set(binaryCacheKey, binaryNullValue)); + + Object result = cache.get(key, () -> { + throw new IllegalStateException(); + }); + + assertThat(result).isNull(); + } + + @ParameterizedRedisTest // GH-2890 + void getWithValueLoaderShouldRetrieveValue() { + + AtomicLong counter = new AtomicLong(); + Object result = cache.get(key, () -> { + counter.incrementAndGet(); + return sample; + }); + + assertThat(result).isEqualTo(sample); + result = cache.get(key, () -> { + counter.incrementAndGet(); + return sample; + }); + + assertThat(result).isEqualTo(sample); + assertThat(counter).hasValue(1); + } + @ParameterizedRedisTest // DATAREDIS-481 void evictShouldRemoveKey() { @@ -358,7 +390,7 @@ void prefixCacheNameCreatesCacheKeyCorrectly() { doWithConnection(connection -> assertThat( connection.stringCommands().get("redis::cache::key-1".getBytes(StandardCharsets.UTF_8))) - .isEqualTo(binarySample)); + .isEqualTo(binarySample)); } @ParameterizedRedisTest // DATAREDIS-715 @@ -435,105 +467,6 @@ void cacheShouldFailOnNonConvertibleCacheKey() { assertThatIllegalStateException().isThrownBy(() -> cache.put(key, sample)); } - @ParameterizedRedisTest // GH-2079 - void multipleThreadsLoadValueOnce() throws InterruptedException { - - int threadCount = 2; - - CountDownLatch prepare = new CountDownLatch(threadCount); - CountDownLatch prepareForReturn = new CountDownLatch(1); - CountDownLatch finished = new CountDownLatch(threadCount); - AtomicInteger retrievals = new AtomicInteger(); - AtomicReference storage = new AtomicReference<>(); - - cache = new RedisCache("foo", new RedisCacheWriter() { - - @Override - public byte[] get(String name, byte[] key) { - return get(name, key, null); - } - - @Override - public byte[] get(String name, byte[] key, @Nullable Duration ttl) { - - prepare.countDown(); - try { - prepareForReturn.await(1, TimeUnit.MINUTES); - } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } - - return storage.get(); - } - - @Override - public CompletableFuture retrieve(String name, byte[] key, @Nullable Duration ttl) { - byte[] value = get(name, key); - return CompletableFuture.completedFuture(value); - } - - @Override - public CompletableFuture store(String name, byte[] key, byte[] value, @Nullable Duration ttl) { - return null; - } - - @Override - public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) { - storage.set(value); - } - - @Override - public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) { - return new byte[0]; - } - - @Override - public void remove(String name, byte[] key) { - - } - - @Override - public void clean(String name, byte[] pattern) { - - } - - @Override - public void clearStatistics(String name) { - - } - - @Override - public RedisCacheWriter withStatisticsCollector(CacheStatisticsCollector cacheStatisticsCollector) { - return null; - } - - @Override - public CacheStatistics getCacheStatistics(String cacheName) { - return null; - } - }, RedisCacheConfiguration.defaultCacheConfig()); - - ThreadPoolExecutor tpe = new ThreadPoolExecutor(threadCount, threadCount, 1, TimeUnit.MINUTES, - new LinkedBlockingDeque<>(), new DefaultThreadFactory("RedisCacheTests")); - - IntStream.range(0, threadCount).forEach(it -> tpe.submit(() -> { - cache.get("foo", retrievals::incrementAndGet); - finished.countDown(); - })); - - // wait until all Threads have arrived in RedisCacheWriter.get(…) - prepare.await(); - - // let all threads continue - prepareForReturn.countDown(); - - // wait until ThreadPoolExecutor has completed. - finished.await(); - tpe.shutdown(); - - assertThat(retrievals).hasValue(1); - } - @EnabledOnCommand("GETEX") @ParameterizedRedisTest // GH-2351 void cacheGetWithTimeToIdleExpirationWhenEntryNotExpiredShouldReturnValue() { From 49542eccee7527085f4ef08fec6fee71767814d3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 29 Jul 2024 09:33:55 +0200 Subject: [PATCH 048/187] =?UTF-8?q?Apply=20time=20to=20idle=20in=20RedisCa?= =?UTF-8?q?che.retrieve(=E2=80=A6).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now correctly apply time to idle on retrievals. Original Pull Request: #2948 --- .../data/redis/cache/RedisCache.java | 7 ++- .../data/redis/cache/RedisCacheWriter.java | 32 ++++++------ .../data/redis/cache/RedisCacheTests.java | 50 ++++++++++++------- 3 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCache.java b/src/main/java/org/springframework/data/redis/cache/RedisCache.java index fb94eb244e..fe747b7aa7 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCache.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCache.java @@ -423,7 +423,12 @@ protected String convertKey(Object key) { } private CompletableFuture retrieveValue(Object key) { - return getCacheWriter().retrieve(getName(), createAndConvertCacheKey(key)) // + + CompletableFuture retrieve = getCacheConfiguration().isTimeToIdleEnabled() + ? getCacheWriter().retrieve(getName(), createAndConvertCacheKey(key), getTimeToLive(key)) + : getCacheWriter().retrieve(getName(), createAndConvertCacheKey(key)); + + return retrieve // .thenApply(binaryValue -> binaryValue != null ? deserializeCacheValue(binaryValue) : null) // .thenApply(this::toValueWrapper); } diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java index fbf7e96dcf..2da307ed8b 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java @@ -213,10 +213,10 @@ default CompletableFuture retrieve(String name, byte[] key) { /** * Write the given key/value pair to Redis and set the expiration time if defined. * - * @param name The cache name must not be {@literal null}. - * @param key The key for the cache entry. Must not be {@literal null}. - * @param value The value stored for the key. Must not be {@literal null}. - * @param ttl Optional expiration time. Can be {@literal null}. + * @param name cache name must not be {@literal null}. + * @param key key for the cache entry. Must not be {@literal null}. + * @param value value stored for the key. Must not be {@literal null}. + * @param ttl optional expiration time. Can be {@literal null}. */ void put(String name, byte[] key, byte[] value, @Nullable Duration ttl); @@ -225,10 +225,10 @@ default CompletableFuture retrieve(String name, byte[] key) { *

* This operation is non-blocking. * - * @param name The cache name must not be {@literal null}. - * @param key The key for the cache entry. Must not be {@literal null}. - * @param value The value stored for the key. Must not be {@literal null}. - * @param ttl Optional expiration time. Can be {@literal null}. + * @param name cache name must not be {@literal null}. + * @param key key for the cache entry. Must not be {@literal null}. + * @param value value stored for the key. Must not be {@literal null}. + * @param ttl optional expiration time. Can be {@literal null}. * @since 3.2 */ CompletableFuture store(String name, byte[] key, byte[] value, @Nullable Duration ttl); @@ -236,10 +236,10 @@ default CompletableFuture retrieve(String name, byte[] key) { /** * Write the given value to Redis if the key does not already exist. * - * @param name The cache name must not be {@literal null}. - * @param key The key for the cache entry. Must not be {@literal null}. - * @param value The value stored for the key. Must not be {@literal null}. - * @param ttl Optional expiration time. Can be {@literal null}. + * @param name cache name must not be {@literal null}. + * @param key key for the cache entry. Must not be {@literal null}. + * @param value value stored for the key. Must not be {@literal null}. + * @param ttl optional expiration time. Can be {@literal null}. * @return {@literal null} if the value has been written, the value stored for the key if it already exists. */ @Nullable @@ -248,16 +248,16 @@ default CompletableFuture retrieve(String name, byte[] key) { /** * Remove the given key from Redis. * - * @param name The cache name must not be {@literal null}. - * @param key The key for the cache entry. Must not be {@literal null}. + * @param name cache name must not be {@literal null}. + * @param key key for the cache entry. Must not be {@literal null}. */ void remove(String name, byte[] key); /** * Remove all keys following the given pattern. * - * @param name The cache name must not be {@literal null}. - * @param pattern The pattern for the keys to remove. Must not be {@literal null}. + * @param name cache name must not be {@literal null}. + * @param pattern pattern for the keys to remove. Must not be {@literal null}. */ void clean(String name, byte[] pattern); diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java index 4dfbd0d1ec..f5face7248 100644 --- a/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java +++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java @@ -16,9 +16,6 @@ package org.springframework.data.redis.cache; import static org.assertj.core.api.Assertions.*; -import static org.awaitility.Awaitility.*; - -import io.netty.util.concurrent.DefaultThreadFactory; import java.io.Serializable; import java.nio.charset.StandardCharsets; @@ -29,20 +26,16 @@ import java.util.Date; import java.util.Objects; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.IntStream; import org.junit.jupiter.api.BeforeEach; + import org.springframework.cache.Cache.ValueWrapper; import org.springframework.cache.interceptor.SimpleKey; import org.springframework.cache.interceptor.SimpleKeyGenerator; @@ -478,11 +471,10 @@ void cacheGetWithTimeToIdleExpirationWhenEntryNotExpiredShouldReturnValue() { assertThat(unwrap(cache.get(this.key))).isEqualTo(this.sample); - for (int count = 0; count < 5; count++) { + doWithConnection(connection -> { - await().atMost(Duration.ofMillis(100)); - assertThat(unwrap(cache.get(this.key))).isEqualTo(this.sample); - } + assertThat(connection.keyCommands().ttl(this.binaryCacheKey)).isGreaterThan(1); + }); } @EnabledOnCommand("GETEX") @@ -496,9 +488,9 @@ void cacheGetWithTimeToIdleExpirationAfterEntryExpiresShouldReturnNull() { assertThat(unwrap(cache.get(this.key))).isEqualTo(this.sample); - await().atMost(Duration.ofMillis(200)); - - assertThat(cache.get(this.cacheKey, Person.class)).isNull(); + doWithConnection(connection -> { + assertThat(connection.keyCommands().ttl(this.binaryCacheKey)).isGreaterThan(1); + }); } @ParameterizedRedisTest // GH-2650 @@ -533,6 +525,30 @@ void retrieveReturnsCachedValue() throws Exception { assertThat(value.get(5, TimeUnit.SECONDS)).isNotNull(); assertThat(value.get().get()).isEqualTo(this.sample); assertThat(value).isDone(); + + doWithConnection(connection -> { + assertThat(connection.keyCommands().ttl(this.binaryCacheKey)).isEqualTo(-1); + }); + } + + @ParameterizedRedisTest // GH-2890 + @EnabledOnRedisDriver(RedisDriver.LETTUCE) + void retrieveAppliesTimeToIdle() throws ExecutionException, InterruptedException { + + doWithConnection(connection -> connection.stringCommands().set(this.binaryCacheKey, this.binarySample)); + + RedisCache cache = new RedisCache("cache", usingRedisCacheWriter(), + usingRedisCacheConfiguration(withTtiExpiration())); + + CompletableFuture value = cache.retrieve(this.key); + + assertThat(value).isNotNull(); + assertThat(value.get().get()).isEqualTo(this.sample); + assertThat(value).isDone(); + + doWithConnection(connection -> { + assertThat(connection.keyCommands().ttl(this.binaryCacheKey)).isGreaterThan(1); + }); } @ParameterizedRedisTest // GH-2650 @@ -689,7 +705,7 @@ private Object unwrap(@Nullable Object value) { private Function withTtiExpiration() { Function entryTtlFunction = cacheConfiguration -> cacheConfiguration - .entryTtl(Duration.ofMillis(100)); + .entryTtl(Duration.ofSeconds(10)); return entryTtlFunction.andThen(RedisCacheConfiguration::enableTimeToIdle); } From 0f4585119057805dc4220c9d6be3ca64bca290c0 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 22 Aug 2024 15:09:04 +0200 Subject: [PATCH 049/187] Polishing. Remove no longer needed wasLocked flag from CacheWriter. Original Pull Request: #2948 --- .../redis/cache/DefaultRedisCacheWriter.java | 28 ++++++++--------- .../DefaultRedisCachWriterUnitTests.java | 31 ++++++++++++++++++- .../cache/DefaultRedisCacheWriterTests.java | 5 ++- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java index e7fe73ab69..f82aa50a76 100644 --- a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java @@ -24,6 +24,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -177,10 +178,8 @@ public byte[] get(String name, byte[] key, Supplier valueLoader, @Nullab return execute(name, connection -> { - boolean wasLocked = false; if (isLockingCacheWriter()) { doLock(name, key, null, connection); - wasLocked = true; } try { @@ -195,7 +194,7 @@ public byte[] get(String name, byte[] key, Supplier valueLoader, @Nullab doPut(connection, name, key, value, ttl); return value; } finally { - if (isLockingCacheWriter() && wasLocked) { + if (isLockingCacheWriter()) { doUnlock(name, connection); } } @@ -274,10 +273,8 @@ public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Durat return execute(name, connection -> { - boolean wasLocked = false; if (isLockingCacheWriter()) { doLock(name, key, value, connection); - wasLocked = true; } try { @@ -299,7 +296,7 @@ public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Durat return connection.stringCommands().get(key); } finally { - if (isLockingCacheWriter() && wasLocked) { + if (isLockingCacheWriter()) { doUnlock(name, connection); } } @@ -324,12 +321,9 @@ public void clean(String name, byte[] pattern) { execute(name, connection -> { - boolean wasLocked = false; - try { if (isLockingCacheWriter()) { doLock(name, name, pattern, connection); - wasLocked = true; } long deleteCount = batchStrategy.cleanCache(connection, name, pattern); @@ -342,7 +336,7 @@ public void clean(String name, byte[] pattern) { statistics.incDeletesBy(name, (int) deleteCount); } finally { - if (wasLocked && isLockingCacheWriter()) { + if (isLockingCacheWriter()) { doUnlock(name, connection); } } @@ -373,10 +367,10 @@ public RedisCacheWriter withStatisticsCollector(CacheStatisticsCollector cacheSt * @param name the name of the cache to lock. */ void lock(String name) { - execute(name, connection -> doLock(name, name, null, connection)); + executeWithoutResult(name, connection -> doLock(name, name, null, connection)); } - boolean doLock(String name, Object contextualKey, @Nullable Object contextualValue, RedisConnection connection) { + void doLock(String name, Object contextualKey, @Nullable Object contextualValue, RedisConnection connection) { RedisStringCommands commands = connection.stringCommands(); Expiration expiration = Expiration.from(this.lockTtl.getTimeToLive(contextualKey, contextualValue)); @@ -386,8 +380,6 @@ boolean doLock(String name, Object contextualKey, @Nullable Object contextualVal true)) { checkAndPotentiallyWaitUntilUnlocked(name, connection); } - - return true; } /** @@ -412,6 +404,14 @@ private T execute(String name, Function callback) { } } + private void executeWithoutResult(String name, Consumer callback) { + + try (RedisConnection connection = this.connectionFactory.getConnection()) { + checkAndPotentiallyWaitUntilUnlocked(name, connection); + callback.accept(connection); + } + } + private T executeLockFree(Function callback) { try (RedisConnection connection = this.connectionFactory.getConnection()) { diff --git a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java index 233333ef83..d05d5bd6b7 100644 --- a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java @@ -16,10 +16,13 @@ package org.springframework.data.redis.cache; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -32,9 +35,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - +import org.springframework.dao.PessimisticLockingFailureException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisKeyCommands; import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.core.types.Expiration; @@ -42,6 +46,7 @@ * Unit tests for {@link DefaultRedisCacheWriter} * * @author John Blum + * @author Christoph Strobl */ @ExtendWith(MockitoExtension.class) class DefaultRedisCacheWriterUnitTests { @@ -109,4 +114,28 @@ void getWithNullTtl() { verify(this.mockConnection, times(1)).close(); verifyNoMoreInteractions(this.mockConnection, mockStringCommands); } + + @Test // GH-2890 + void mustNotUnlockWhenLockingFails() { + + byte[] key = "TestKey".getBytes(); + byte[] value = "TestValue".getBytes(); + + RedisStringCommands mockStringCommands = mock(RedisStringCommands.class); + RedisKeyCommands mockKeyCommands = mock(RedisKeyCommands.class); + + doReturn(mockStringCommands).when(this.mockConnection).stringCommands(); + doReturn(mockKeyCommands).when(this.mockConnection).keyCommands(); + doThrow(new PessimisticLockingFailureException("you-shall-not-pass")).when(mockStringCommands).set(any(byte[].class), + any(byte[].class), any(), any()); + + RedisCacheWriter cacheWriter = spy( + new DefaultRedisCacheWriter(this.mockConnectionFactory, Duration.ofMillis(10), mock(BatchStrategy.class)) + .withStatisticsCollector(this.mockCacheStatisticsCollector)); + + assertThatException() + .isThrownBy(() -> cacheWriter.get("TestCache", key, () -> value, Duration.ofMillis(10), false)); + + verify(mockKeyCommands, never()).del(any()); + } } diff --git a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java index e903b24bc6..4d6c2cd0fe 100644 --- a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java +++ b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java @@ -453,13 +453,12 @@ void doLockShouldGetLock() throws InterruptedException { DefaultRedisCacheWriter cw = new DefaultRedisCacheWriter(connectionFactory, Duration.ofMillis(10), BatchStrategies.keys()) { - boolean doLock(String name, Object contextualKey, @Nullable Object contextualValue, RedisConnection connection) { + void doLock(String name, Object contextualKey, @Nullable Object contextualValue, RedisConnection connection) { - boolean doLock = super.doLock(name, contextualKey, contextualValue, connection); + super.doLock(name, contextualKey, contextualValue, connection); // any concurrent access (aka not waiting until the lock is acquired) will result in a concurrency greater 1 assertThat(concurrency.incrementAndGet()).isOne(); - return doLock; } @Nullable From 803fd7101e3184d02c27569b9c1376a42a49f97d Mon Sep 17 00:00:00 2001 From: Junghoon Ban Date: Tue, 10 Sep 2024 09:35:14 +0200 Subject: [PATCH 050/187] Refactor Redis secondary-index key formatting. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Redis secondary-index keys are now formatted using helper methods instead of using chains of concatAll(toBytes(…)) sequences for more readability. Original pull request: #2795 Closes: #2794 --- .../data/redis/core/IndexWriter.java | 64 +++++++++++++++---- .../data/redis/core/RedisQueryEngine.java | 15 ++--- .../data/redis/core/convert/IndexedData.java | 9 +++ 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/IndexWriter.java b/src/main/java/org/springframework/data/redis/core/IndexWriter.java index a7f1cf5f60..3d3b048de5 100644 --- a/src/main/java/org/springframework/data/redis/core/IndexWriter.java +++ b/src/main/java/org/springframework/data/redis/core/IndexWriter.java @@ -33,23 +33,26 @@ /** * {@link IndexWriter} takes care of writing secondary index structures to - * Redis. Depending on the type of {@link IndexedData} it uses eg. Sets with specific names to add actually referenced - * keys to. While doing so {@link IndexWriter} also keeps track of all indexes associated with the root types key, which + * Redis. Depending on the type of {@link IndexedData}, it uses Sets with specific names to add actually referenced keys + * to. While doing so {@link IndexWriter} also keeps track of all indexes associated with the root types key, which * allows to remove the root key from all indexes in case of deletion. * * @author Christoph Strobl * @author Rob Winch + * @author Mark Paluch * @since 1.7 */ class IndexWriter { + private static final byte[] SEPARATOR = ":".getBytes(); + private static final byte[] IDX = "idx".getBytes(); + private final RedisConnection connection; private final RedisConverter converter; /** * Creates new {@link IndexWriter}. * - * @param keyspace The key space to write index values to. Must not be {@literal null}. * @param connection must not be {@literal null}. * @param converter must not be {@literal null}. */ @@ -127,7 +130,7 @@ public void removeKeyFromIndexes(String keyspace, Object key) { Assert.notNull(key, "Key must not be null"); byte[] binKey = toBytes(key); - byte[] indexHelperKey = ByteUtils.concatAll(toBytes(keyspace + ":"), binKey, toBytes(":idx")); + byte[] indexHelperKey = createIndexKey(keyspace, binKey); for (byte[] indexKey : connection.sMembers(indexHelperKey)) { @@ -147,10 +150,10 @@ public void removeKeyFromIndexes(String keyspace, Object key) { */ public void removeAllIndexes(String keyspace) { - Set potentialIndex = connection.keys(toBytes(keyspace + ":*")); + Set potentialIndex = connection.keys(createIndexKey(keyspace, "*")); if (!potentialIndex.isEmpty()) { - connection.del(potentialIndex.toArray(new byte[potentialIndex.size()][])); + connection.del(potentialIndex.toArray(new byte[0][])); } } @@ -162,7 +165,7 @@ private void removeKeyFromExistingIndexes(byte[] key, Iterable inde } /** - * Remove given key from all indexes matching {@link IndexedData#getIndexName()}: + * Remove given key from all indexes matching {@link IndexedData#getIndexName()}. * * @param key * @param indexedData @@ -171,8 +174,7 @@ protected void removeKeyFromExistingIndexes(byte[] key, IndexedData indexedData) Assert.notNull(indexedData, "IndexedData must not be null"); - Set existingKeys = connection - .keys(toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName() + ":*")); + Set existingKeys = connection.keys(createIndexKey(indexedData.getKeyPrefix(), "*")); if (!CollectionUtils.isEmpty(existingKeys)) { for (byte[] existingKey : existingKeys) { @@ -216,12 +218,12 @@ protected void addKeyToIndex(byte[] key, IndexedData indexedData) { return; } - byte[] indexKey = toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName() + ":"); + byte[] indexKey = toBytes(indexedData.getKeyPrefix(), SEPARATOR); indexKey = ByteUtils.concat(indexKey, toBytes(value)); connection.sAdd(indexKey, key); // keep track of indexes used for the object - connection.sAdd(ByteUtils.concatAll(toBytes(indexedData.getKeyspace() + ":"), key, toBytes(":idx")), indexKey); + connection.sAdd(createIndexKey(indexedData.getKeyspace(), key), indexKey); } else if (indexedData instanceof GeoIndexedPropertyValue propertyValue) { Object value = propertyValue.getValue(); @@ -229,17 +231,53 @@ protected void addKeyToIndex(byte[] key, IndexedData indexedData) { return; } - byte[] indexKey = toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName()); + byte[] indexKey = toBytes(indexedData.getKeyPrefix()); connection.geoAdd(indexKey, propertyValue.getPoint(), key); // keep track of indexes used for the object - connection.sAdd(ByteUtils.concatAll(toBytes(indexedData.getKeyspace() + ":"), key, toBytes(":idx")), indexKey); + connection.sAdd(createIndexKey(indexedData.getKeyspace(), key), indexKey); } else { throw new IllegalArgumentException( String.format("Cannot write index data for unknown index type %s", indexedData.getClass())); } } + private byte[] createIndexKey(String keyspace, byte[] key) { + return createIndexKey(keyspace, key, IDX); + } + + private byte[] createIndexKey(Object... items) { + + Object[] elements = new Object[items.length + (items.length - 1)]; + + int j = 0; + for (int i = 0; i < items.length; i++) { + + elements[j++] = items[i]; + if (items.length - 1 > i) { + elements[j++] = SEPARATOR; + } + } + + return toBytes(elements); + } + + private byte[] toBytes(Object... values) { + + byte[][] arrays = new byte[values.length][]; + + for (int i = 0; i < values.length; i++) { + + if (values[i] instanceof byte[] bb) { + arrays[i] = bb; + } else { + arrays[i] = toBytes(values[i]); + } + } + + return ByteUtils.concatAll(arrays); + } + private byte[] toBytes(@Nullable Object source) { if (source == null) { diff --git a/src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java b/src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java index 07b946d2b9..64dfe66e97 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java +++ b/src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Set; +import org.springframework.core.convert.ConversionService; import org.springframework.data.geo.Circle; import org.springframework.data.geo.GeoResult; import org.springframework.data.geo.GeoResults; @@ -81,8 +82,8 @@ private RedisQueryEngine(CriteriaAccessor criteriaAccessor, @Override @SuppressWarnings("unchecked") - public List execute(RedisOperationChain criteria, Comparator sort, long offset, int rows, - String keyspace, Class type) { + public List execute(RedisOperationChain criteria, Comparator sort, long offset, int rows, String keyspace, + Class type) { List result = doFind(criteria, offset, rows, keyspace, type); if (sort != null) { @@ -199,8 +200,7 @@ private List findKeys(RedisOperationChain criteria, int rows, String key } @Override - public List execute(RedisOperationChain criteria, Comparator sort, long offset, int rows, - String keyspace) { + public List execute(RedisOperationChain criteria, Comparator sort, long offset, int rows, String keyspace) { return execute(criteria, sort, offset, rows, keyspace, Object.class); } @@ -229,14 +229,13 @@ public long count(RedisOperationChain criteria, String keyspace) { private byte[][] keys(String prefix, Collection source) { + ConversionService conversionService = getRequiredAdapter().getConverter().getConversionService(); byte[][] keys = new byte[source.size()][]; int i = 0; for (PathAndValue pathAndValue : source) { - byte[] convertedValue = getRequiredAdapter().getConverter().getConversionService() - .convert(pathAndValue.getFirstValue(), byte[].class); - byte[] fullPath = getRequiredAdapter().getConverter().getConversionService() - .convert(prefix + pathAndValue.getPath() + ":", byte[].class); + byte[] convertedValue = conversionService.convert(pathAndValue.getFirstValue(), byte[].class); + byte[] fullPath = conversionService.convert(prefix + pathAndValue.getPath() + ":", byte[].class); keys[i] = ByteUtils.concat(fullPath, convertedValue); i++; diff --git a/src/main/java/org/springframework/data/redis/core/convert/IndexedData.java b/src/main/java/org/springframework/data/redis/core/convert/IndexedData.java index e6c0fce9e5..122bdfd2c7 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/IndexedData.java +++ b/src/main/java/org/springframework/data/redis/core/convert/IndexedData.java @@ -38,4 +38,13 @@ public interface IndexedData { */ String getKeyspace(); + /** + * Return the key prefix for usage in Redis. + * + * @return concatenated form of the keyspace and the index name. + * @since 3.3.4 + */ + default String getKeyPrefix() { + return getKeyspace() + ":" + getIndexName(); + } } From 6190b3bff60dae57df96fa0798a902eb6374be34 Mon Sep 17 00:00:00 2001 From: Junghoon Ban Date: Wed, 29 Nov 2023 17:09:00 +0900 Subject: [PATCH 051/187] Improve readability in `RedisQueryCreator`. Closes #2788 --- .../repository/query/RedisQueryCreator.java | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java b/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java index bb5d7dae69..ae38848978 100644 --- a/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java +++ b/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java @@ -29,6 +29,7 @@ import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; /** @@ -37,6 +38,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author John Blum + * @author Junghoon Ban * @since 1.7 */ public class RedisQueryCreator extends AbstractQueryCreator, RedisOperationChain> { @@ -78,16 +80,16 @@ protected RedisOperationChain or(RedisOperationChain base, RedisOperationChain c } @Override - protected KeyValueQuery complete(final RedisOperationChain criteria, Sort sort) { + protected KeyValueQuery complete(@Nullable final RedisOperationChain criteria, Sort sort) { KeyValueQuery query = new KeyValueQuery<>(criteria); - if (query.getCriteria() != null && !CollectionUtils.isEmpty(query.getCriteria().getSismember()) - && !CollectionUtils.isEmpty(query.getCriteria().getOrSismember())) - if (query.getCriteria().getSismember().size() == 1 && query.getCriteria().getOrSismember().size() == 1) { + if (criteria != null && !CollectionUtils.isEmpty(criteria.getSismember()) + && !CollectionUtils.isEmpty(criteria.getOrSismember())) + if (criteria.getSismember().size() == 1 && criteria.getOrSismember().size() == 1) { - query.getCriteria().getOrSismember().add(query.getCriteria().getSismember().iterator().next()); - query.getCriteria().getSismember().clear(); + criteria.getOrSismember().add(criteria.getSismember().iterator().next()); + criteria.getSismember().clear(); } if (sort.isSorted()) { @@ -99,43 +101,39 @@ protected KeyValueQuery complete(final RedisOperationChain private NearPath getNearPath(Part part, Iterator iterator) { + String path = part.getProperty().toDotPath(); Object value = iterator.next(); - Point point; - Distance distance; - - if (value instanceof Circle) { - point = ((Circle) value).getCenter(); - distance = ((Circle) value).getRadius(); - } else if (value instanceof Point) { + if (value instanceof Circle circle) { + return new NearPath(path, circle.getCenter(), circle.getRadius()); + } - point = (Point) value; + if (value instanceof Point point) { if (!iterator.hasNext()) { String message = "Expected to find distance value for geo query; Are you missing a parameter"; throw new InvalidDataAccessApiUsageException(message); } + Distance distance; Object distObject = iterator.next(); - if (distObject instanceof Distance) { - distance = (Distance) distObject; - } else if (distObject instanceof Number) { - distance = new Distance(((Number) distObject).doubleValue(), Metrics.KILOMETERS); + + if (distObject instanceof Distance distanceValue) { + distance = distanceValue; + } else if (distObject instanceof Number numberValue) { + distance = new Distance(numberValue.doubleValue(), Metrics.KILOMETERS); } else { String message = String.format("Expected to find Distance or Numeric value for geo query but was %s", distObject.getClass()); - throw new InvalidDataAccessApiUsageException(message); } - } else { - - String message = String.format("Expected to find a Circle or Point/Distance for geo query but was %s.", - value.getClass()); - throw new InvalidDataAccessApiUsageException(message); + return new NearPath(path, point, distance); } - return new NearPath(part.getProperty().toDotPath(), point, distance); + String message = String.format("Expected to find a Circle or Point/Distance for geo query but was %s.", + value.getClass()); + throw new InvalidDataAccessApiUsageException(message); } } From 15f541bf4cad6212bc99ca2035ecaa263f9378cf Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 10 Sep 2024 10:12:43 +0200 Subject: [PATCH 052/187] Polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve pattern variable naming, use ClassUtils.getDescriptiveType(…) to avoid null-pointer exceptions. Simplify RedisOperationChain condition checks. See #2788 --- .../repository/query/RedisQueryCreator.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java b/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java index ae38848978..0479654a88 100644 --- a/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java +++ b/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.repository.query; +import java.util.Collection; import java.util.Iterator; import org.springframework.dao.InvalidDataAccessApiUsageException; @@ -30,6 +31,7 @@ import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.PartTree; import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; /** @@ -80,17 +82,15 @@ protected RedisOperationChain or(RedisOperationChain base, RedisOperationChain c } @Override - protected KeyValueQuery complete(@Nullable final RedisOperationChain criteria, Sort sort) { + protected KeyValueQuery complete(@Nullable RedisOperationChain criteria, Sort sort) { KeyValueQuery query = new KeyValueQuery<>(criteria); - if (criteria != null && !CollectionUtils.isEmpty(criteria.getSismember()) - && !CollectionUtils.isEmpty(criteria.getOrSismember())) - if (criteria.getSismember().size() == 1 && criteria.getOrSismember().size() == 1) { - - criteria.getOrSismember().add(criteria.getSismember().iterator().next()); - criteria.getSismember().clear(); - } + if (criteria != null && containsExactlyOne(criteria.getSismember()) + && containsExactlyOne(criteria.getOrSismember())) { + criteria.getOrSismember().addAll(criteria.getSismember()); + criteria.getSismember().clear(); + } if (sort.isSorted()) { query.setSort(sort); @@ -111,29 +111,33 @@ private NearPath getNearPath(Part part, Iterator iterator) { if (value instanceof Point point) { if (!iterator.hasNext()) { - String message = "Expected to find distance value for geo query; Are you missing a parameter"; + String message = "Expected to find distance value for geo query; Are you missing a parameter?"; throw new InvalidDataAccessApiUsageException(message); } Distance distance; Object distObject = iterator.next(); - if (distObject instanceof Distance distanceValue) { - distance = distanceValue; - } else if (distObject instanceof Number numberValue) { - distance = new Distance(numberValue.doubleValue(), Metrics.KILOMETERS); + if (distObject instanceof Distance dist) { + distance = dist; + } else if (distObject instanceof Number num) { + distance = new Distance(num.doubleValue(), Metrics.KILOMETERS); } else { String message = String.format("Expected to find Distance or Numeric value for geo query but was %s", - distObject.getClass()); + ClassUtils.getDescriptiveType(distObject)); throw new InvalidDataAccessApiUsageException(message); } return new NearPath(path, point, distance); } - String message = String.format("Expected to find a Circle or Point/Distance for geo query but was %s.", - value.getClass()); + String message = String.format("Expected to find a Circle or Point/Distance for geo query but was %s", + ClassUtils.getDescriptiveType(value.getClass())); throw new InvalidDataAccessApiUsageException(message); } + + private static boolean containsExactlyOne(Collection collection) { + return !CollectionUtils.isEmpty(collection) && collection.size() == 1; + } } From b7f26fa30f80925cb6fad293a84cb55e6b7e13ef Mon Sep 17 00:00:00 2001 From: jinkshower Date: Sat, 7 Sep 2024 17:12:30 +0900 Subject: [PATCH 053/187] Fix `XAddOptions` maxlen handling and `XPendingOptions` validation. Closes #2982 Original pull request: #2985 --- .../connection/ReactiveStreamCommands.java | 22 ++++++---- .../redis/connection/RedisStreamCommands.java | 15 +++++-- .../connection/stream/StreamReadOptions.java | 2 +- .../ReactiveStreamCommandsUnitTests.java | 41 +++++++++++++++++++ .../RedisStreamCommandsUnitTests.java | 36 ++++++++++++++++ 5 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java create mode 100644 src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java index f6e5687cf9..db05734500 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java @@ -337,7 +337,7 @@ public Long getMaxlen() { * @since 2.3 */ public boolean hasMaxlen() { - return maxlen != null && maxlen > 0; + return maxlen != null; } /** @@ -685,7 +685,7 @@ default Mono xPending(ByteBuffer key, String groupName) Assert.notNull(key, "Key must not be null"); Assert.notNull(groupName, "GroupName must not be null"); - return xPendingSummary(Mono.just(new PendingRecordsCommand(key, groupName, null, Range.unbounded(), null))).next() + return xPendingSummary(Mono.just(PendingRecordsCommand.pending(key, groupName))).next() .map(CommandResponse::getOutput); } @@ -726,7 +726,7 @@ default Mono xPending(ByteBuffer key, Consumer consumer) { */ @Nullable default Mono xPending(ByteBuffer key, String groupName, String consumerName) { - return xPending(Mono.just(new PendingRecordsCommand(key, groupName, consumerName, Range.unbounded(), null))).next() + return xPending(Mono.just(PendingRecordsCommand.pending(key, groupName).consumer(consumerName))).next() .map(CommandResponse::getOutput); } @@ -743,7 +743,7 @@ default Mono xPending(ByteBuffer key, String groupName, String * @since 2.3 */ default Mono xPending(ByteBuffer key, String groupName, Range range, Long count) { - return xPending(Mono.just(new PendingRecordsCommand(key, groupName, null, range, count))).next() + return xPending(Mono.just(PendingRecordsCommand.pending(key, groupName).range(range, count))).next() .map(CommandResponse::getOutput); } @@ -779,8 +779,8 @@ default Mono xPending(ByteBuffer key, Consumer consumer, Range< */ default Mono xPending(ByteBuffer key, String groupName, String consumerName, Range range, Long count) { - return xPending(Mono.just(new PendingRecordsCommand(key, groupName, consumerName, range, count))).next() - .map(CommandResponse::getOutput); + return xPending(Mono.just(PendingRecordsCommand.pending(key, groupName).consumer(consumerName).range(range, count))) + .next().map(CommandResponse::getOutput); } /** @@ -832,9 +832,15 @@ static PendingRecordsCommand pending(ByteBuffer key, String groupName) { /** * Create new {@link PendingRecordsCommand} with given {@link Range} and limit. * + * @param range must not be {@literal null}. + * @param count the max number of messages to return. Must not be negative. * @return new instance of {@link XPendingOptions}. */ - public PendingRecordsCommand range(Range range, Long count) { + public PendingRecordsCommand range(Range range, Long count) { + + Assert.notNull(range, "Range must not be null"); + Assert.isTrue(count > -1, "Count must not be negative"); + return new PendingRecordsCommand(getKey(), groupName, consumerName, range, count); } @@ -886,7 +892,7 @@ public boolean hasConsumer() { * @return {@literal true} count is set. */ public boolean isLimited() { - return count != null && count > -1; + return count != null; } } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java index df9cbd9f52..df097158b0 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java @@ -214,7 +214,7 @@ public Long getMaxlen() { * @return {@literal true} if {@literal MAXLEN} is set. */ public boolean hasMaxlen() { - return maxlen != null && maxlen > 0; + return maxlen != null; } /** @@ -788,19 +788,28 @@ public static XPendingOptions unbounded() { /** * Create new {@link XPendingOptions} with an unbounded {@link Range} ({@literal - +}). * - * @param count the max number of messages to return. Must not be {@literal null}. + * @param count the max number of messages to return. Must not be negative. * @return new instance of {@link XPendingOptions}. */ public static XPendingOptions unbounded(Long count) { + + Assert.isTrue(count > -1, "Count must not be negative"); + return new XPendingOptions(null, Range.unbounded(), count); } /** * Create new {@link XPendingOptions} with given {@link Range} and limit. * + * @param range must not be {@literal null}. + * @param count the max number of messages to return. Must not be negative. * @return new instance of {@link XPendingOptions}. */ public static XPendingOptions range(Range range, Long count) { + + Assert.notNull(range, "Range must not be null"); + Assert.isTrue(count > -1, "Count must not be negative"); + return new XPendingOptions(null, range, count); } @@ -848,7 +857,7 @@ public boolean hasConsumer() { * @return {@literal true} count is set. */ public boolean isLimited() { - return count != null && count > -1; + return count != null; } } diff --git a/src/main/java/org/springframework/data/redis/connection/stream/StreamReadOptions.java b/src/main/java/org/springframework/data/redis/connection/stream/StreamReadOptions.java index 45c456c0bc..58fd0652e8 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/StreamReadOptions.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/StreamReadOptions.java @@ -96,7 +96,7 @@ public StreamReadOptions block(Duration timeout) { */ public StreamReadOptions count(long count) { - Assert.isTrue(count > 0, "Count must be greater or equal to zero"); + Assert.isTrue(count > 0, "Count must be greater than zero"); return new StreamReadOptions(block, count, noack); } diff --git a/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java new file mode 100644 index 0000000000..bef26ac866 --- /dev/null +++ b/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java @@ -0,0 +1,41 @@ +package org.springframework.data.redis.connection; + +import static org.assertj.core.api.Assertions.*; + +import java.nio.ByteBuffer; + +import org.junit.jupiter.api.Test; + +import org.springframework.data.domain.Range; +import org.springframework.data.redis.connection.ReactiveStreamCommands.PendingRecordsCommand; + +/** + * Unit tests for {@link ReactiveStreamCommands}. + * + * @author jinkshower + */ +class ReactiveStreamCommandsUnitTests { + + @Test // GH-2982 + void pendingRecordsCommandRangeShouldThrowExceptionWhenRangeIsNull() { + + ByteBuffer key = ByteBuffer.wrap("my-stream".getBytes()); + String groupName = "my-group"; + + PendingRecordsCommand command = PendingRecordsCommand.pending(key, groupName); + + assertThatThrownBy(() -> command.range(null, 10L)).isInstanceOf(IllegalArgumentException.class); + } + + @Test // GH-2982 + void pendingRecordsCommandRangeShouldThrowExceptionWhenCountIsNegative() { + + ByteBuffer key = ByteBuffer.wrap("my-stream".getBytes()); + String groupName = "my-group"; + + PendingRecordsCommand command = PendingRecordsCommand.pending(key, groupName); + Range range = Range.closed("0", "10"); + + assertThatThrownBy(() -> command.range(range, -1L)).isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java new file mode 100644 index 0000000000..635f6cbe12 --- /dev/null +++ b/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java @@ -0,0 +1,36 @@ +package org.springframework.data.redis.connection; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +import org.springframework.data.domain.Range; +import org.springframework.data.redis.connection.RedisStreamCommands.XPendingOptions; + +/** + * Unit tests for {@link RedisStreamCommands}. + * + * @author jinkshower + */ +class RedisStreamCommandsUnitTests { + + @Test // GH-2982 + void xPendingOptionsUnboundedShouldThrowExceptionWhenCountIsNegative() { + + assertThatThrownBy(() -> XPendingOptions.unbounded(-1L)).isInstanceOf(IllegalArgumentException.class); + } + + @Test // GH-2982 + void xPendingOptionsRangeShouldThrowExceptionWhenRangeIsNull() { + + assertThatThrownBy(() -> XPendingOptions.range(null, 10L)).isInstanceOf(IllegalArgumentException.class); + } + + @Test // GH-2982 + void xPendingOptionsRangeShouldThrowExceptionWhenCountIsNegative() { + + Range range = Range.closed("0", "10"); + + assertThatThrownBy(() -> XPendingOptions.range(range, -1L)).isInstanceOf(IllegalArgumentException.class); + } +} From 6eac1d1ad0593c790b4768c7811e6b2a660f13df Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 10 Sep 2024 10:55:01 +0200 Subject: [PATCH 054/187] Polishing. Refine assertions usage. Original pull request: #2985 Closes #2982 --- .../ReactiveStreamCommandsUnitTests.java | 19 +++- .../RedisStreamCommandsUnitTests.java | 23 ++++- ...ctiveStreamOperationsIntegrationTests.java | 99 +++++++------------ ...faultStreamOperationsIntegrationTests.java | 26 +++-- 4 files changed, 89 insertions(+), 78 deletions(-) diff --git a/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java index bef26ac866..9912b92b3c 100644 --- a/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java @@ -1,3 +1,18 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.data.redis.connection; import static org.assertj.core.api.Assertions.*; @@ -24,7 +39,7 @@ void pendingRecordsCommandRangeShouldThrowExceptionWhenRangeIsNull() { PendingRecordsCommand command = PendingRecordsCommand.pending(key, groupName); - assertThatThrownBy(() -> command.range(null, 10L)).isInstanceOf(IllegalArgumentException.class); + assertThatIllegalArgumentException().isThrownBy(() -> command.range(null, 10L)); } @Test // GH-2982 @@ -36,6 +51,6 @@ void pendingRecordsCommandRangeShouldThrowExceptionWhenCountIsNegative() { PendingRecordsCommand command = PendingRecordsCommand.pending(key, groupName); Range range = Range.closed("0", "10"); - assertThatThrownBy(() -> command.range(range, -1L)).isInstanceOf(IllegalArgumentException.class); + assertThatIllegalArgumentException().isThrownBy(() -> command.range(range, -1L)); } } diff --git a/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java index 635f6cbe12..e8daac5071 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java @@ -1,3 +1,18 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.data.redis.connection; import static org.assertj.core.api.Assertions.*; @@ -16,14 +31,12 @@ class RedisStreamCommandsUnitTests { @Test // GH-2982 void xPendingOptionsUnboundedShouldThrowExceptionWhenCountIsNegative() { - - assertThatThrownBy(() -> XPendingOptions.unbounded(-1L)).isInstanceOf(IllegalArgumentException.class); + assertThatIllegalArgumentException().isThrownBy(() -> XPendingOptions.unbounded(-1L)); } @Test // GH-2982 void xPendingOptionsRangeShouldThrowExceptionWhenRangeIsNull() { - - assertThatThrownBy(() -> XPendingOptions.range(null, 10L)).isInstanceOf(IllegalArgumentException.class); + assertThatIllegalArgumentException().isThrownBy(() -> XPendingOptions.range(null, 10L)); } @Test // GH-2982 @@ -31,6 +44,6 @@ void xPendingOptionsRangeShouldThrowExceptionWhenCountIsNegative() { Range range = Range.closed("0", "10"); - assertThatThrownBy(() -> XPendingOptions.range(range, -1L)).isInstanceOf(IllegalArgumentException.class); + assertThatIllegalArgumentException().isThrownBy(() -> XPendingOptions.range(range, -1L)); } } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java index 27cf52f0f7..3e3c7d504d 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java @@ -82,12 +82,6 @@ public class DefaultReactiveStreamOperationsIntegrationTests { return ReactiveOperationsTestParams.testParams(); } - /** - * @param redisTemplate - * @param keyFactory - * @param valueFactory - * @param label parameterized test label, no further use besides that. - */ public DefaultReactiveStreamOperationsIntegrationTests(Fixture fixture) { this.serializer = fixture.getSerializer(); @@ -208,27 +202,25 @@ void addMaxLenShouldLimitMessagesSize() { RecordId messageId = streamOperations.add(key, Collections.singletonMap(hashKey, newValue), options).block(); - streamOperations.range(key, Range.unbounded()).as(StepVerifier::create) - .consumeNextWith(actual -> { + streamOperations.range(key, Range.unbounded()).as(StepVerifier::create).consumeNextWith(actual -> { - assertThat(actual.getId()).isEqualTo(messageId); - assertThat(actual.getStream()).isEqualTo(key); - assertThat(actual).hasSize(1); + assertThat(actual.getId()).isEqualTo(messageId); + assertThat(actual.getStream()).isEqualTo(key); + assertThat(actual).hasSize(1); - if (!(key instanceof byte[] || value instanceof byte[])) { - assertThat(actual.getValue()).containsEntry(hashKey, newValue); - } + if (!(key instanceof byte[] || value instanceof byte[])) { + assertThat(actual.getValue()).containsEntry(hashKey, newValue); + } - }) - .verifyComplete(); + }).verifyComplete(); } @ParameterizedRedisTest // GH-2915 void addMaxLenShouldLimitSimpleMessagesSize() { assumeTrue(!(serializer instanceof Jackson2JsonRedisSerializer) - && !(serializer instanceof GenericJackson2JsonRedisSerializer) - && !(serializer instanceof JdkSerializationRedisSerializer) && !(serializer instanceof OxmSerializer)); + && !(serializer instanceof GenericJackson2JsonRedisSerializer) + && !(serializer instanceof JdkSerializationRedisSerializer) && !(serializer instanceof OxmSerializer)); K key = keyFactory.instance(); HV value = valueFactory.instance(); @@ -241,31 +233,29 @@ void addMaxLenShouldLimitSimpleMessagesSize() { RecordId messageId = streamOperations.add(StreamRecords.objectBacked(newValue).withStreamKey(key), options).block(); streamOperations.range((Class) value.getClass(), key, Range.unbounded()).as(StepVerifier::create) - .consumeNextWith(actual -> { + .consumeNextWith(actual -> { - assertThat(actual.getId()).isEqualTo(messageId); - assertThat(actual.getStream()).isEqualTo(key); - assertThat(actual.getValue()).isEqualTo(newValue); + assertThat(actual.getId()).isEqualTo(messageId); + assertThat(actual.getStream()).isEqualTo(key); + assertThat(actual.getValue()).isEqualTo(newValue); - }) - .expectNextCount(0) - .verifyComplete(); + }).expectNextCount(0).verifyComplete(); } @ParameterizedRedisTest // GH-2915 void addMaxLenShouldLimitSimpleMessageWithRawSerializerSize() { assumeTrue(!(serializer instanceof Jackson2JsonRedisSerializer) - && !(serializer instanceof GenericJackson2JsonRedisSerializer)); + && !(serializer instanceof GenericJackson2JsonRedisSerializer)); SerializationPair keySerializer = redisTemplate.getSerializationContext().getKeySerializationPair(); RedisSerializationContext serializationContext = RedisSerializationContext - . newSerializationContext(StringRedisSerializer.UTF_8).key(keySerializer) - .hashValue(SerializationPair.raw()).hashKey(SerializationPair.raw()).build(); + . newSerializationContext(StringRedisSerializer.UTF_8).key(keySerializer) + .hashValue(SerializationPair.raw()).hashKey(SerializationPair.raw()).build(); ReactiveRedisTemplate raw = new ReactiveRedisTemplate<>(redisTemplate.getConnectionFactory(), - serializationContext); + serializationContext); K key = keyFactory.instance(); Person value = new PersonObjectFactory().instance(); @@ -275,18 +265,17 @@ void addMaxLenShouldLimitSimpleMessageWithRawSerializerSize() { Person newValue = new PersonObjectFactory().instance(); XAddOptions options = XAddOptions.maxlen(1).approximateTrimming(false); - RecordId messageId = raw.opsForStream().add(StreamRecords.objectBacked(newValue).withStreamKey(key), options).block(); + RecordId messageId = raw.opsForStream().add(StreamRecords.objectBacked(newValue).withStreamKey(key), options) + .block(); raw.opsForStream().range((Class) value.getClass(), key, Range.unbounded()).as(StepVerifier::create) - .consumeNextWith(it -> { + .consumeNextWith(it -> { - assertThat(it.getId()).isEqualTo(messageId); - assertThat(it.getStream()).isEqualTo(key); - assertThat(it.getValue()).isEqualTo(newValue); + assertThat(it.getId()).isEqualTo(messageId); + assertThat(it.getStream()).isEqualTo(key); + assertThat(it.getValue()).isEqualTo(newValue); - }) - .expectNextCount(0) - .verifyComplete(); + }).expectNextCount(0).verifyComplete(); } @ParameterizedRedisTest // GH-2915 @@ -303,17 +292,13 @@ void addMinIdShouldEvictLowerIdMessages() { RecordId messageId2 = streamOperations.add(key, Collections.singletonMap(hashKey, value), options).block(); - streamOperations.range(key, Range.unbounded()).as(StepVerifier::create) - .consumeNextWith(actual -> { - assertThat(actual.getId()).isEqualTo(messageId1); - assertThat(actual.getStream()).isEqualTo(key); - }) - .consumeNextWith(actual -> { - assertThat(actual.getId()).isEqualTo(messageId2); - assertThat(actual.getStream()).isEqualTo(key); - }) - .expectNextCount(0) - .verifyComplete(); + streamOperations.range(key, Range.unbounded()).as(StepVerifier::create).consumeNextWith(actual -> { + assertThat(actual.getId()).isEqualTo(messageId1); + assertThat(actual.getStream()).isEqualTo(key); + }).consumeNextWith(actual -> { + assertThat(actual.getId()).isEqualTo(messageId2); + assertThat(actual.getStream()).isEqualTo(key); + }).expectNextCount(0).verifyComplete(); } @ParameterizedRedisTest // GH-2915 @@ -327,13 +312,9 @@ void addMakeNoStreamShouldNotCreateStreamWhenNoStreamExists() { streamOperations.add(key, Collections.singletonMap(hashKey, value), options).block(); - streamOperations.size(key).as(StepVerifier::create) - .expectNext(0L) - .verifyComplete(); + streamOperations.size(key).as(StepVerifier::create).expectNext(0L).verifyComplete(); - streamOperations.range(key, Range.unbounded()).as(StepVerifier::create) - .expectNextCount(0L) - .verifyComplete(); + streamOperations.range(key, Range.unbounded()).as(StepVerifier::create).expectNextCount(0L).verifyComplete(); } @ParameterizedRedisTest // GH-2915 @@ -349,13 +330,9 @@ void addMakeNoStreamShouldCreateStreamWhenStreamExists() { streamOperations.add(key, Collections.singletonMap(hashKey, value), options).block(); - streamOperations.size(key).as(StepVerifier::create) - .expectNext(2L) - .verifyComplete(); + streamOperations.size(key).as(StepVerifier::create).expectNext(2L).verifyComplete(); - streamOperations.range(key, Range.unbounded()).as(StepVerifier::create) - .expectNextCount(2L) - .verifyComplete(); + streamOperations.range(key, Range.unbounded()).as(StepVerifier::create).expectNextCount(2L).verifyComplete(); } @ParameterizedRedisTest // DATAREDIS-864 @@ -525,7 +502,6 @@ void pendingShouldReadMessageDetails() { assertThat(pending.get(0).getConsumerName()).isEqualTo("my-consumer"); assertThat(pending.get(0).getTotalDeliveryCount()).isOne(); }).verifyComplete(); - } @ParameterizedRedisTest // GH-2465 @@ -550,6 +526,5 @@ void claimShouldReadMessageDetails() { assertThat(claimed.getValue()).isEqualTo(content); assertThat(claimed.getId()).isEqualTo(messageId); }).verifyComplete(); - } } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java index 06ab8c5982..9aa146d9ca 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java @@ -36,7 +36,16 @@ import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.connection.lettuce.extension.LettuceConnectionFactoryExtension; -import org.springframework.data.redis.connection.stream.*; +import org.springframework.data.redis.connection.stream.Consumer; +import org.springframework.data.redis.connection.stream.MapRecord; +import org.springframework.data.redis.connection.stream.ObjectRecord; +import org.springframework.data.redis.connection.stream.PendingMessages; +import org.springframework.data.redis.connection.stream.PendingMessagesSummary; +import org.springframework.data.redis.connection.stream.ReadOffset; +import org.springframework.data.redis.connection.stream.RecordId; +import org.springframework.data.redis.connection.stream.StreamOffset; +import org.springframework.data.redis.connection.stream.StreamReadOptions; +import org.springframework.data.redis.connection.stream.StreamRecords; import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.condition.EnabledOnRedisDriver; import org.springframework.data.redis.test.condition.EnabledOnRedisVersion; @@ -67,7 +76,7 @@ public class DefaultStreamOperationsIntegrationTests { private final StreamOperations streamOps; public DefaultStreamOperationsIntegrationTests(RedisTemplate redisTemplate, ObjectFactory keyFactory, - ObjectFactory objectFactory) { + ObjectFactory objectFactory) { this.redisTemplate = redisTemplate; this.connectionFactory = redisTemplate.getRequiredConnectionFactory(); @@ -83,7 +92,7 @@ public static Collection testParams() { params.addAll(AbstractOperationsTestParams .testParams(JedisConnectionFactoryExtension.getConnectionFactory(RedisStanalone.class))); - if(RedisDetector.isClusterAvailable()) { + if (RedisDetector.isClusterAvailable()) { params.addAll(AbstractOperationsTestParams .testParams(JedisConnectionFactoryExtension.getConnectionFactory(RedisCluster.class))); } @@ -91,7 +100,7 @@ public static Collection testParams() { params.addAll(AbstractOperationsTestParams .testParams(LettuceConnectionFactoryExtension.getConnectionFactory(RedisStanalone.class))); - if(RedisDetector.isClusterAvailable()) { + if (RedisDetector.isClusterAvailable()) { params.addAll(AbstractOperationsTestParams .testParams(LettuceConnectionFactoryExtension.getConnectionFactory(RedisCluster.class))); } @@ -456,7 +465,8 @@ void readShouldReadSimpleMessage() { RecordId messageId1 = streamOps.add(StreamRecords.objectBacked(value).withStreamKey(key)); streamOps.add(StreamRecords.objectBacked(value).withStreamKey(key)); - List> messages = streamOps.read((Class) value.getClass(), StreamOffset.create(key, ReadOffset.from("0-0"))); + List> messages = streamOps.read((Class) value.getClass(), + StreamOffset.create(key, ReadOffset.from("0-0"))); assertThat(messages).hasSize(2); @@ -535,8 +545,7 @@ void pendingShouldReadMessageSummary() { RecordId messageId = streamOps.add(key, Collections.singletonMap(hashKey, value)); streamOps.createGroup(key, ReadOffset.from("0-0"), "my-group"); - streamOps.read(Consumer.from("my-group", "my-consumer"), - StreamOffset.create(key, ReadOffset.lastConsumed())); + streamOps.read(Consumer.from("my-group", "my-consumer"), StreamOffset.create(key, ReadOffset.lastConsumed())); PendingMessagesSummary pending = streamOps.pending(key, "my-group"); @@ -554,8 +563,7 @@ void pendingShouldReadMessageDetails() { RecordId messageId = streamOps.add(key, Collections.singletonMap(hashKey, value)); streamOps.createGroup(key, ReadOffset.from("0-0"), "my-group"); - streamOps.read(Consumer.from("my-group", "my-consumer"), - StreamOffset.create(key, ReadOffset.lastConsumed())); + streamOps.read(Consumer.from("my-group", "my-consumer"), StreamOffset.create(key, ReadOffset.lastConsumed())); PendingMessages pending = streamOps.pending(key, "my-group", Range.unbounded(), 10L); From 44aa79e2b243df0f94a0dd65de30ca188cee70e7 Mon Sep 17 00:00:00 2001 From: John Blum Date: Thu, 19 Oct 2023 14:53:44 -0700 Subject: [PATCH 055/187] =?UTF-8?q?Replace=20use=20of=20`String.format(?= =?UTF-8?q?=E2=80=A6)`=20with=20formatted=20Strings.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original pull request: #2752 Closes #2751 --- .../data/redis/ClusterRedirectException.java | 2 +- .../redis/cache/DefaultRedisCacheWriter.java | 4 +- .../data/redis/cache/RedisCache.java | 18 +++---- .../redis/cache/RedisCacheConfiguration.java | 8 ++- .../connection/ClusterCommandExecutor.java | 13 ++--- .../redis/connection/ClusterTopology.java | 8 +-- .../data/redis/connection/RedisNode.java | 13 +++-- .../data/redis/connection/RedisPassword.java | 2 +- .../RedisSentinelConfiguration.java | 4 +- .../connection/RedisSocketConfiguration.java | 2 +- .../RedisStandaloneConfiguration.java | 4 +- ...RedisStaticMasterReplicaConfiguration.java | 2 +- .../redis/connection/convert/Converters.java | 2 +- .../jedis/JedisClusterConnection.java | 8 +-- .../jedis/JedisClusterServerCommands.java | 7 +-- .../jedis/JedisClusterZSetCommands.java | 18 ++++--- .../jedis/JedisConnectionFactory.java | 10 ++-- .../connection/jedis/JedisConverters.java | 6 +-- .../connection/jedis/JedisServerCommands.java | 2 +- .../connection/jedis/JedisZSetCommands.java | 16 +++--- .../lettuce/ClusterConnectionProvider.java | 5 +- .../lettuce/LettuceClusterConnection.java | 6 +-- .../connection/lettuce/LettuceConnection.java | 14 ++--- .../lettuce/LettuceConnectionFactory.java | 8 +-- .../connection/lettuce/LettuceConverters.java | 4 +- .../LettucePoolingConnectionProvider.java | 5 +- .../lettuce/LettuceReactiveListCommands.java | 2 +- .../LettuceReactivePubSubCommands.java | 2 +- .../LettuceReactiveServerCommands.java | 2 +- .../LettuceReactiveStringCommands.java | 2 +- .../connection/lettuce/LettuceScanCursor.java | 4 +- .../lettuce/LettuceServerCommands.java | 3 +- .../lettuce/LettuceZSetCommands.java | 16 +++--- ...StaticMasterReplicaConnectionProvider.java | 4 +- .../MicrometerTracingAdapter.java | 6 +-- .../redis/connection/stream/Consumer.java | 2 +- .../connection/stream/StreamRecords.java | 2 +- .../data/redis/core/IndexWriter.java | 9 ++-- .../data/redis/core/RedisCommand.java | 16 +++--- .../data/redis/core/RedisConnectionUtils.java | 4 +- .../redis/core/convert/BinaryConverters.java | 2 +- .../core/convert/MappingRedisConverter.java | 20 ++++--- .../redis/core/index/GeoIndexDefinition.java | 4 +- .../mapping/BasicRedisPersistentEntity.java | 12 ++--- .../core/mapping/RedisMappingContext.java | 16 +++--- .../data/redis/core/script/DigestUtils.java | 3 +- .../redis/core/types/RedisClientInfo.java | 3 +- .../data/redis/domain/geo/BoundingBox.java | 2 +- .../data/redis/hash/BeanUtilsHashMapper.java | 2 +- .../data/redis/hash/Jackson2HashMapper.java | 3 +- .../RedisMessageListenerContainer.java | 15 +++--- .../adapter/MessageListenerAdapter.java | 9 ++-- .../data/redis/repository/cdi/CdiBean.java | 5 +- .../cdi/RedisRepositoryExtension.java | 25 ++++----- .../core/MappingRedisEntityInformation.java | 5 +- .../repository/query/ExampleQueryMapper.java | 4 +- .../repository/query/RedisQueryCreator.java | 21 ++++---- .../serializer/DefaultRedisElementWriter.java | 6 ++- .../GenericJackson2JsonRedisSerializer.java | 6 +-- .../redis/stream/DefaultStreamReceiver.java | 52 +++++++++---------- .../support/atomic/RedisAtomicDouble.java | 3 +- .../support/atomic/RedisAtomicInteger.java | 3 +- .../redis/support/atomic/RedisAtomicLong.java | 3 +- .../collections/AbstractRedisCollection.java | 9 +--- .../collections/ReversedRedisListView.java | 5 +- .../data/redis/util/RedisAssertions.java | 4 +- .../data/redis/SettingsUtils.java | 12 +++-- .../connection/ClusterSlotHashUtilsTests.java | 2 +- .../JedisConnectionIntegrationTests.java | 4 +- .../LettuceConnectionFactoryTests.java | 9 ++-- ...tiveScriptingCommandsIntegrationTests.java | 2 +- .../redis/core/RedisKeyValueAdapterTests.java | 2 +- .../BasicRedisPersistentEntityUnitTests.java | 6 +-- .../EnabledOnRedisAvailableCondition.java | 4 +- .../EnabledOnRedisClusterCondition.java | 4 +- .../EnabledOnRedisDriverCondition.java | 7 ++- .../EnabledOnRedisSentinelCondition.java | 7 ++- .../EnabledOnRedisVersionCondition.java | 15 +++--- .../ParameterizedRedisTestExtension.java | 23 ++++---- .../ParameterizedTestNameFormatter.java | 5 +- .../data/redis/test/util/MockitoUtils.java | 2 +- .../data/redis/test/util/RedisTestData.java | 2 +- 82 files changed, 285 insertions(+), 328 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/ClusterRedirectException.java b/src/main/java/org/springframework/data/redis/ClusterRedirectException.java index 3f8abc5811..ee90d0f890 100644 --- a/src/main/java/org/springframework/data/redis/ClusterRedirectException.java +++ b/src/main/java/org/springframework/data/redis/ClusterRedirectException.java @@ -46,7 +46,7 @@ public class ClusterRedirectException extends DataRetrievalFailureException { */ public ClusterRedirectException(int slot, String targetHost, int targetPort, Throwable e) { - super(String.format("Redirect: slot %s to %s:%s.", slot, targetHost, targetPort), e); + super("Redirect: slot %s to %s:%s.".formatted(slot, targetHost, targetPort), e); this.slot = slot; this.host = targetHost; diff --git a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java index f82aa50a76..f5a73e6ea6 100644 --- a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java @@ -445,9 +445,7 @@ private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection c // Re-interrupt current Thread to allow other participants to react. Thread.currentThread().interrupt(); - String message = String.format("Interrupted while waiting to unlock cache %s", name); - - throw new PessimisticLockingFailureException(message, ex); + throw new PessimisticLockingFailureException("Interrupted while waiting to unlock cache %s".formatted(name), ex); } finally { this.statistics.incLockTime(name, System.nanoTime() - lockWaitTimeNs); } diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCache.java b/src/main/java/org/springframework/data/redis/cache/RedisCache.java index fe747b7aa7..c900387fee 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCache.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCache.java @@ -298,12 +298,9 @@ private Object processAndCheckValue(@Nullable Object value) { Object cacheValue = preProcessCacheValue(value); if (nullCacheValueIsNotAllowed(cacheValue)) { - - String message = String.format("Cache '%s' does not allow 'null' values; Avoid storing null" + throw new IllegalArgumentException(("Cache '%s' does not allow 'null' values; Avoid storing null" + " via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null'" - + " via RedisCacheConfiguration", getName()); - - throw new IllegalArgumentException(message); + + " via RedisCacheConfiguration").formatted(getName())); } return cacheValue; @@ -414,12 +411,9 @@ protected String convertKey(Object key) { return key.toString(); } - String message = String.format( - "Cannot convert cache key %s to String; Please register a suitable Converter" - + " via 'RedisCacheConfiguration.configureKeyConverters(...)' or override '%s.toString()'", - source, key.getClass().getName()); - - throw new IllegalStateException(message); + throw new IllegalStateException(("Cannot convert cache key %s to String; Please register a suitable Converter" + + " via 'RedisCacheConfiguration.configureKeyConverters(...)' or override '%s.toString()'") + .formatted(source, key.getClass().getName())); } private CompletableFuture retrieveValue(Object key) { @@ -484,7 +478,7 @@ private String convertCollectionLikeOrMapKey(Object key, TypeDescriptor source) return "[" + stringJoiner + "]"; } - throw new IllegalArgumentException(String.format("Cannot convert cache key [%s] to String", key)); + throw new IllegalArgumentException("Cannot convert cache key [%s] to String".formatted(key)); } private byte[] createAndConvertCacheKey(Object key) { diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java index 261c932ab2..a8bf4cd44a 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java @@ -424,11 +424,9 @@ public void addCacheKeyConverter(Converter cacheKeyConverter) { public void configureKeyConverters(Consumer registryConsumer) { if (!(getConversionService() instanceof ConverterRegistry)) { - - String message = "'%s' returned by getConversionService() does not allow Converter registration;" - + " Please make sure to provide a ConversionService that implements ConverterRegistry"; - - throw new IllegalStateException(String.format(message, getConversionService().getClass().getName())); + throw new IllegalStateException(("'%s' returned by getConversionService() does not allow Converter registration;" + + " Please make sure to provide a ConversionService that implements ConverterRegistry") + .formatted(getConversionService().getClass().getName())); } registryConsumer.accept((ConverterRegistry) getConversionService()); diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java index 3e896e53d9..4bbf07bc83 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java @@ -130,12 +130,9 @@ private NodeResult executeCommandOnSingleNode(ClusterCommandCallback this.maxRedirects) { - - String message = String.format("Cannot follow Cluster Redirects over more than %s legs; " - + "Consider increasing the number of redirects to follow; Current value is: %s.", - redirectCount, this.maxRedirects); - - throw new TooManyClusterRedirectionsException(message); + throw new TooManyClusterRedirectionsException(("Cannot follow Cluster Redirects over more than %s legs;" + + " Consider increasing the number of redirects to follow; Current value is: %s") + .formatted(redirectCount, this.maxRedirects)); } RedisClusterNode nodeToUse = lookupNode(node); @@ -178,7 +175,7 @@ private RedisClusterNode lookupNode(RedisClusterNode node) { try { return topologyProvider.getTopology().lookup(node); } catch (ClusterStateFailureException ex) { - throw new IllegalArgumentException(String.format("Node %s is unknown to cluster", node), ex); + throw new IllegalArgumentException("Node %s is unknown to cluster".formatted(node), ex); } } @@ -215,7 +212,7 @@ public MultiNodeResult executeCommandAsyncOnNodes(ClusterCommandCallba try { resolvedRedisClusterNodes.add(topology.lookup(node)); } catch (ClusterStateFailureException ex) { - throw new IllegalArgumentException(String.format("Node %s is unknown to cluster", node), ex); + throw new IllegalArgumentException("Node %s is unknown to cluster".formatted(node), ex); } } diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterTopology.java b/src/main/java/org/springframework/data/redis/connection/ClusterTopology.java index bb82facae2..1ea254bdb2 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterTopology.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterTopology.java @@ -141,7 +141,7 @@ public RedisClusterNode getKeyServingMasterNode(byte[] key) { } throw new ClusterStateFailureException( - String.format("Could not find master node serving slot %s for key '%s',", slot, Arrays.toString(key))); + "Could not find master node serving slot %s for key '%s',".formatted(slot, Arrays.toString(key))); } /** @@ -161,7 +161,7 @@ public RedisClusterNode lookup(String host, int port) { } throw new ClusterStateFailureException( - String.format("Could not find node at %s:%s; Is your cluster info up to date", host, port)); + "Could not find node at %s:%d; Is your cluster info up to date".formatted(host, port)); } /** @@ -182,7 +182,7 @@ public RedisClusterNode lookup(String nodeId) { } throw new ClusterStateFailureException( - String.format("Could not find node at %s; Is your cluster info up to date", nodeId)); + "Could not find node at %s; Is your cluster info up to date".formatted(nodeId)); } /** @@ -210,7 +210,7 @@ public RedisClusterNode lookup(RedisClusterNode node) { } throw new ClusterStateFailureException( - String.format("Could not find node at %s; Have you provided either host and port or the nodeId", node)); + ("Could not find node at %s;" + " Have you provided either host and port or the nodeId").formatted(node)); } /** diff --git a/src/main/java/org/springframework/data/redis/connection/RedisNode.java b/src/main/java/org/springframework/data/redis/connection/RedisNode.java index ad6841054f..2ea0e6dd18 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisNode.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisNode.java @@ -102,11 +102,11 @@ public static RedisNode fromString(String hostPortString) { try { port = Integer.parseInt(portString); } catch (RuntimeException ignore) { - throw new IllegalArgumentException(String.format("Unparseable port number: %s", hostPortString)); + throw new IllegalArgumentException("Unparseable port number: %s".formatted(hostPortString)); } if (!isValidPort(port)) { - throw new IllegalArgumentException(String.format("Port number out of range: %s", hostPortString)); + throw new IllegalArgumentException("Port number out of range: %s".formatted(hostPortString)); } return new RedisNode(host, port); @@ -123,14 +123,14 @@ private static String[] getHostAndPortFromBracketedHost(String hostPortString) { if (hostPortString.charAt(0) != '[') { throw new IllegalArgumentException( - String.format("Bracketed host-port string must start with a bracket: %s", hostPortString)); + "Bracketed host-port string must start with a bracket: %s".formatted(hostPortString)); } int colonIndex = hostPortString.indexOf(':'); int closeBracketIndex = hostPortString.lastIndexOf(']'); if (!(colonIndex > -1 && closeBracketIndex > colonIndex)) { - throw new IllegalArgumentException(String.format("Invalid bracketed host/port: %s", hostPortString)); + throw new IllegalArgumentException("Invalid bracketed host/port: %s".formatted(hostPortString)); } String host = hostPortString.substring(1, closeBracketIndex); @@ -138,12 +138,11 @@ private static String[] getHostAndPortFromBracketedHost(String hostPortString) { return new String[] { host, "" }; } else { if (!(hostPortString.charAt(closeBracketIndex + 1) == ':')) { - throw new IllegalArgumentException( - String.format("Only a colon may follow a close bracket: %s", hostPortString)); + throw new IllegalArgumentException("Only a colon may follow a close bracket: %s".formatted(hostPortString)); } for (int i = closeBracketIndex + 2; i < hostPortString.length(); ++i) { if (!Character.isDigit(hostPortString.charAt(i))) { - throw new IllegalArgumentException(String.format("Port must be numeric: %s", hostPortString)); + throw new IllegalArgumentException("Port must be numeric: %s".formatted(hostPortString)); } } return new String[] { host, hostPortString.substring(closeBracketIndex + 2) }; diff --git a/src/main/java/org/springframework/data/redis/connection/RedisPassword.java b/src/main/java/org/springframework/data/redis/connection/RedisPassword.java index 89c8713ea9..56229c84ff 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisPassword.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisPassword.java @@ -140,7 +140,7 @@ public Optional toOptional() { @Override public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), isPresent() ? "*****" : ""); + return "%s[%s]".formatted(getClass().getSimpleName(), isPresent() ? "*****" : ""); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java index 24b390724d..f9683193c6 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java @@ -150,7 +150,7 @@ public RedisSentinelConfiguration(PropertySource propertySource) { try { database = Integer.parseInt(databaseSource); } catch (NumberFormatException ex) { - throw new IllegalArgumentException(String.format("Invalid DB index '%s'; integer required", databaseSource)); + throw new IllegalArgumentException("Invalid DB index '%s'; integer required".formatted(databaseSource)); } this.setDatabase(database); } @@ -266,7 +266,7 @@ public int getDatabase() { @Override public void setDatabase(int index) { - Assert.isTrue(index >= 0, () -> String.format("Invalid DB index '%d'; non-negative index required", index)); + Assert.isTrue(index >= 0, "Invalid DB index '%d'; non-negative index required".formatted(index)); this.database = index; } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java index 5e41c44a7b..4f9a0336db 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java @@ -74,7 +74,7 @@ public int getDatabase() { @Override public void setDatabase(int index) { - Assert.isTrue(index >= 0, () -> String.format("Invalid DB index '%s' (a positive index required)", index)); + Assert.isTrue(index >= 0, () -> "Invalid DB index '%s'; non-negative index required".formatted(index)); this.database = index; } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java index f7d8b2372a..d106dda372 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java @@ -70,7 +70,7 @@ public RedisStandaloneConfiguration(String hostName, int port) { Assert.hasText(hostName, "Host name must not be null or empty"); Assert.isTrue(port >= 1 && port <= 65535, - () -> String.format("Port %d must be a valid TCP port in the range between 1-65535", port)); + "Port %d must be a valid TCP port in the range between 1-65535".formatted(port)); this.hostName = hostName; this.port = port; @@ -103,7 +103,7 @@ public int getDatabase() { @Override public void setDatabase(int index) { - Assert.isTrue(index >= 0, () -> String.format("Invalid DB index '%s' (a positive index required)", index)); + Assert.isTrue(index >= 0, "Invalid DB index '%d'; non-negative index required".formatted(index)); this.database = index; } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java index 1b60bf7a94..f3033f8176 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java @@ -118,7 +118,7 @@ public int getDatabase() { @Override public void setDatabase(int index) { - Assert.isTrue(index >= 0, () -> String.format("Invalid DB index '%s' (a positive index required)", index)); + Assert.isTrue(index >= 0, "Invalid DB index '%d'; non-negative index required".formatted(index)); this.database = index; this.nodes.forEach(it -> it.setDatabase(database)); 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 98ec282faf..b0ef79f896 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 @@ -410,7 +410,7 @@ public static Object parse(Object source, String sourcePath, Map entry : errors.entrySet()) { - stringBuilder.append(String.format("\r\n\t- %s failed: %s", entry.getKey(), entry.getValue().getMessage())); + stringBuilder.append("\r\n\t- %s failed: %s".formatted(entry.getKey(), entry.getValue().getMessage())); } throw new ClusterStateFailureException( 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 c0b61ae75d..a48a35092a 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 @@ -347,10 +347,11 @@ public Long time(RedisClusterNode node, TimeUnit timeUnit) { public void killClient(String host, int port) { Assert.hasText(host, "Host for 'CLIENT KILL' must not be 'null' or 'empty'"); - String hostAndPort = String.format("%s:%s", host, port); + String hostAndPort = "%s:%d".formatted(host, port); - connection.getClusterCommandExecutor() - .executeCommandOnAllNodes((JedisClusterCommandCallback) client -> client.clientKill(hostAndPort)); + JedisClusterCommandCallback command = client -> client.clientKill(hostAndPort); + + connection.getClusterCommandExecutor().executeCommandOnAllNodes(command); } @Override 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 156fb87fb5..5d9970973a 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 @@ -908,8 +908,9 @@ public Set zInterWithScores(Aggregate aggregate, Weights weights, byte[]. Assert.notNull(sets, "Sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + () -> "The number of weights %d must match the number of source sets %d".formatted(weights.size(), + sets.length)); if (ClusterSlotHashUtil.isSameSlotForAllKeys(sets)) { @@ -951,8 +952,8 @@ public Long zInterStore(byte[] destKey, Aggregate aggregate, Weights weights, by Assert.notNull(destKey, "Destination key must not be null"); Assert.notNull(sets, "Source sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights %d must match the number of source sets %d".formatted(weights.size(), sets.length)); byte[][] allKeys = ByteUtils.mergeArrays(destKey, sets); @@ -1008,8 +1009,9 @@ public Set zUnionWithScores(Aggregate aggregate, Weights weights, byte[]. Assert.notNull(sets, "Sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + () -> "The number of weights %d must match the number of source sets %d".formatted(weights.size(), + sets.length)); if (ClusterSlotHashUtil.isSameSlotForAllKeys(sets)) { @@ -1052,8 +1054,8 @@ public Long zUnionStore(byte[] destKey, Aggregate aggregate, Weights weights, by Assert.notNull(destKey, "Destination key must not be null"); Assert.notNull(sets, "Source sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights %d must match the number of source sets %d".formatted(weights.size(), sets.length)); byte[][] allKeys = ByteUtils.mergeArrays(destKey, sets); diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java index 4e83c271cf..21bd50df5e 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java @@ -1003,7 +1003,7 @@ private Jedis getActiveSentinel() { return jedis; } } catch (Exception ex) { - log.warn(String.format("Ping failed for sentinel host: %s", node.getHost()), ex); + log.warn("Ping failed for sentinel host: %s".formatted(node.getHost()), ex); } finally { if (!success && jedis != null) { jedis.close(); @@ -1040,8 +1040,8 @@ private int getConnectTimeout() { private MutableJedisClientConfiguration getMutableConfiguration() { Assert.state(clientConfiguration instanceof MutableJedisClientConfiguration, - () -> String.format("Client configuration must be instance of MutableJedisClientConfiguration but is %s", - ClassUtils.getShortName(clientConfiguration.getClass()))); + () -> "Client configuration must be instance of MutableJedisClientConfiguration but is %s" + .formatted(ClassUtils.getShortName(clientConfiguration.getClass()))); return (MutableJedisClientConfiguration) clientConfiguration; } @@ -1056,10 +1056,10 @@ private void assertInitialized() { switch (current) { case CREATED, STOPPED -> throw new IllegalStateException( - String.format("JedisConnectionFactory has been %s. Use start() to initialize it", current)); + "JedisConnectionFactory has been %s. Use start() to initialize it".formatted(current)); case DESTROYED -> throw new IllegalStateException("JedisConnectionFactory was destroyed and cannot be used anymore"); - default -> throw new IllegalStateException(String.format("JedisConnectionFactory is %s", current)); + default -> throw new IllegalStateException("JedisConnectionFactory is %s".formatted(current)); } } 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 426d360575..6ffa002cbe 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 @@ -472,7 +472,7 @@ private static byte[] boundaryToBytes(org.springframework.data.domain.Range.Boun } else if (theValue instanceof String string) { value = toBytes(string); } else { - throw new IllegalArgumentException(String.format("Cannot convert %s to binary format", boundary.getValue())); + throw new IllegalArgumentException("Cannot convert %s to binary format".formatted(boundary.getValue())); } ByteBuffer buffer = ByteBuffer.allocate(prefix.length + value.length); @@ -770,7 +770,7 @@ private static GeoSearchParam getGeoSearchParam(GeoShape predicate, GeoSearchPar return param; } - throw new IllegalArgumentException(String.format("Cannot convert %s to Jedis GeoSearchParam", predicate)); + throw new IllegalArgumentException("Cannot convert %s to Jedis GeoSearchParam".formatted(predicate)); } private static void configureGeoReference(GeoReference reference, GeoSearchParam param) { @@ -787,7 +787,7 @@ private static void configureGeoReference(GeoReference reference, GeoSea return; } - throw new IllegalArgumentException(String.format("Cannot extract Geo Reference from %s", reference)); + throw new IllegalArgumentException("Cannot extract Geo Reference from %s".formatted(reference)); } /** 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 bab0522591..3aa8fb9eab 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 @@ -161,7 +161,7 @@ public void killClient(String host, int port) { Assert.hasText(host, "Host for 'CLIENT KILL' must not be 'null' or 'empty'"); - connection.invokeStatus().just(it -> it.clientKill(String.format("%s:%s", host, port))); + connection.invokeStatus().just(it -> it.clientKill("%s:%s".formatted(host, port))); } @Override 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 749e198bcb..cbb4eb40d8 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 @@ -474,8 +474,8 @@ public Set zInterWithScores(Aggregate aggregate, Weights weights, byte[]. Assert.notNull(sets, "Sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights (%d) must match the number of source sets (%d)".formatted(weights.size(), sets.length)); return connection.invoke().fromMany(Jedis::zinterWithScores, PipelineBinaryCommands::zinterWithScores, toZParams(aggregate, weights), sets).toSet(JedisConverters::toTuple); @@ -487,8 +487,8 @@ public Long zInterStore(byte[] destKey, Aggregate aggregate, Weights weights, by Assert.notNull(destKey, "Destination key must not be null"); Assert.notNull(sets, "Source sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights %d must match the number of source sets %d".formatted(weights.size(), sets.length)); ZParams zparams = toZParams(aggregate, weights); @@ -528,8 +528,8 @@ public Set zUnionWithScores(Aggregate aggregate, Weights weights, byte[]. Assert.notNull(sets, "Sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights %d must match the number of source sets %d".formatted(weights.size(), sets.length)); return connection.invoke().fromMany(Jedis::zunionWithScores, PipelineBinaryCommands::zunionWithScores, toZParams(aggregate, weights), sets).toSet(JedisConverters::toTuple); @@ -542,8 +542,8 @@ public Long zUnionStore(byte[] destKey, Aggregate aggregate, Weights weights, by Assert.notNull(sets, "Source sets must not be null"); Assert.notNull(weights, "Weights must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights %d must match the number of source sets %d".formatted(weights.size(), sets.length)); ZParams zparams = toZParams(aggregate, weights); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java index 0b2061f5a7..3426bb4cf2 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java @@ -120,9 +120,8 @@ private Optional getReadFrom() { }); } - String message = String.format("Connection type %s not supported", connectionType); - - return LettuceFutureUtils.failed(new InvalidDataAccessApiUsageException(message)); + return LettuceFutureUtils + .failed(new InvalidDataAccessApiUsageException("Connection type %s not supported".formatted(connectionType))); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java index 3756e9f8b4..f02cfe00e7 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java @@ -216,10 +216,8 @@ private RedisClusterClient getClient() { return (RedisClusterClient) redisClientProvider.getRedisClient(); } - String message = String.format("Connection provider %s does not implement RedisClientProvider", - connectionProvider.getClass().getName()); - - throw new IllegalStateException(message); + throw new IllegalStateException("Connection provider %s does not implement RedisClientProvider" + .formatted(connectionProvider.getClass().getName())); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java index c3ecbde730..0016e73a4c 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java @@ -328,7 +328,7 @@ public Object execute(String command, byte[]... args) { @SuppressWarnings({ "rawtypes", "unchecked" }) public Object execute(String command, @Nullable CommandOutput commandOutputTypeHint, byte[]... args) { - Assert.hasText(command, () -> String.format("A valid command [%s] needs to be specified", command)); + Assert.hasText(command, () -> "A valid command [%s] needs to be specified".formatted(command)); ProtocolKeyword commandType = getCommandType(command.trim().toUpperCase()); @@ -937,9 +937,7 @@ RedisClusterCommands getDedicatedConnection() { return statefulClusterConnection.sync(); } - String message = String.format("%s is not a supported connection type", connection.getClass().getName()); - - throw new IllegalStateException(message); + throw new IllegalStateException("%s is not a supported connection type".formatted(connection.getClass().getName())); } protected RedisClusterAsyncCommands getAsyncDedicatedConnection() { @@ -953,13 +951,12 @@ protected RedisClusterAsyncCommands getAsyncDedicatedConnection( if (connection instanceof StatefulRedisConnection statefulConnection) { return statefulConnection.async(); } + if (asyncDedicatedConnection instanceof StatefulRedisClusterConnection statefulClusterConnection) { return statefulClusterConnection.async(); } - String message = String.format("%s is not a supported connection type", connection.getClass().getName()); - - throw new IllegalStateException(message); + throw new IllegalStateException("%s is not a supported connection type".formatted(connection.getClass().getName())); } @SuppressWarnings("unchecked") @@ -1091,8 +1088,7 @@ private void validateCommand(ProtocolKeyword command, @Nullable byte[]... args) try { redisCommand.validateArgumentCount(args != null ? args.length : 0); } catch (IllegalArgumentException ex) { - String message = String.format("Validation failed for %s command", command); - throw new InvalidDataAccessApiUsageException(message, ex); + throw new InvalidDataAccessApiUsageException("Validation failed for %s command".formatted(command), ex); } } } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java index e673c8257d..fdbc882a99 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java @@ -1444,10 +1444,10 @@ private void assertStarted() { switch (current) { case CREATED, STOPPED -> throw new IllegalStateException( - String.format("LettuceConnectionFactory has been %s. Use start() to initialize it", current)); + "LettuceConnectionFactory has been %s. Use start() to initialize it".formatted(current)); case DESTROYED -> throw new IllegalStateException("LettuceConnectionFactory was destroyed and cannot be used anymore"); - default -> throw new IllegalStateException(String.format("LettuceConnectionFactory is %s", current)); + default -> throw new IllegalStateException("LettuceConnectionFactory is %s".formatted(current)); } } @@ -1500,8 +1500,8 @@ private RedisURI.Builder applyAuthentication(RedisURI.Builder builder) { private MutableLettuceClientConfiguration getMutableConfiguration() { Assert.state(clientConfiguration instanceof MutableLettuceClientConfiguration, - () -> String.format("Client configuration must be instance of MutableLettuceClientConfiguration but is %s", - ClassUtils.getShortName(clientConfiguration.getClass()))); + () -> "Client configuration must be instance of MutableLettuceClientConfiguration but is %s" + .formatted(ClassUtils.getShortName(clientConfiguration.getClass()))); return (MutableLettuceClientConfiguration) clientConfiguration; } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java index 450045a01e..bb443b1166 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java @@ -871,7 +871,7 @@ static GeoSearch.GeoPredicate toGeoPredicate(GeoShape predicate) { toGeoArgsUnit(boxPredicate.getMetric())); } - throw new IllegalArgumentException(String.format("Cannot convert %s to Lettuce GeoPredicate", predicate)); + throw new IllegalArgumentException("Cannot convert %s to Lettuce GeoPredicate".formatted(predicate)); } static GeoSearch.GeoRef toGeoRef(GeoReference reference) { @@ -885,7 +885,7 @@ static GeoSearch.GeoRef toGeoRef(GeoReference reference) { return GeoSearch.fromCoordinates(coordinates.getLongitude(), coordinates.getLatitude()); } - throw new IllegalArgumentException(String.format("Cannot convert %s to Lettuce GeoRef", reference)); + throw new IllegalArgumentException("Cannot convert %s to Lettuce GeoRef".formatted(reference)); } static FlushMode toFlushMode(@Nullable RedisServerCommands.FlushOption option) { diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java index 5b888e8f0e..79e6b5e836 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java @@ -135,9 +135,8 @@ public AbstractRedisClient getRedisClient() { return ((RedisClientProvider) connectionProvider).getRedisClient(); } - throw new IllegalStateException( - String.format("Underlying connection provider %s does not implement RedisClientProvider", - connectionProvider.getClass().getName())); + throw new IllegalStateException("Underlying connection provider %s does not implement RedisClientProvider" + .formatted(connectionProvider.getClass().getName())); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java index 2721877f4c..e0f6f416c3 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java @@ -74,7 +74,7 @@ public Flux> push(Publisher comm if (!command.getUpsert() && command.getValues().size() > 1) { throw new InvalidDataAccessApiUsageException( - String.format("%s PUSHX only allows one value", command.getDirection())); + "%s PUSHX only allows one value".formatted(command.getDirection())); } Mono pushResult; diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommands.java index 7e25074679..67ca0b0b81 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommands.java @@ -233,7 +233,7 @@ static boolean deallocate(ByteBuffer buffer, Map targe @Override public String toString() { - return String.format("%s: Subscribers: %s", new String(raw), SUBSCRIBERS.get(this)); + return "%s: Subscribers: %s".formatted(new String(raw), SUBSCRIBERS.get(this)); } } } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommands.java index f3aa9bd2af..c88df347e0 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommands.java @@ -154,7 +154,7 @@ public Mono killClient(String host, int port) { Assert.notNull(host, "Host must not be null or empty"); - return connection.execute(c -> c.clientKill(String.format("%s:%s", host, port))).next(); + return connection.execute(c -> c.clientKill("%s:%d".formatted(host, port))).next(); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java index 5008c5e68d..7beb8a3bd2 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java @@ -364,7 +364,7 @@ public Flux> bitOp(Publisher c Assert.isTrue(sourceKeys.length == 1, "BITOP NOT does not allow more than 1 source key."); yield reactiveCommands.bitopNot(destinationKey, sourceKeys[0]); } - default -> throw new IllegalArgumentException(String.format("Unknown BITOP '%s'", command.getBitOp())); + default -> throw new IllegalArgumentException("Unknown BITOP '%s'".formatted(command.getBitOp())); }; return result.map(value -> new NumericResponse<>(command, value)); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScanCursor.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScanCursor.java index 6f5ed1a945..50ae5712bc 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScanCursor.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScanCursor.java @@ -59,8 +59,8 @@ protected ScanIteration doScan(CursorId cursorId, ScanOptions options) { } } - throw new IllegalArgumentException(String.format("Current scan %s state and cursor %d do not match", - state != null ? state.getCursor() : "(none)", cursorId)); + throw new IllegalArgumentException("Current scan %s state and cursor %d do not match" + .formatted(state != null ? state.getCursor() : "(none)", cursorId)); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java index 54a1dcfd6d..005fc8c38f 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java @@ -26,7 +26,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisServerCommands; import org.springframework.data.redis.core.types.RedisClientInfo; @@ -173,7 +172,7 @@ public void killClient(String host, int port) { Assert.hasText(host, "Host for 'CLIENT KILL' must not be 'null' or 'empty'"); - String client = String.format("%s:%s", host, port); + String client = "%s:%d".formatted(host, port); connection.invoke().just(RedisServerAsyncCommands::clientKill, client); } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java index 9ebccac7b9..e29aa50dc5 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java @@ -441,8 +441,8 @@ public Set zInterWithScores(Aggregate aggregate, Weights weights, byte[]. Assert.notNull(sets, "Sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights %d must match the number of source sets %d".formatted(weights.size(), sets.length)); ZAggregateArgs zAggregateArgs = zAggregateArgs(aggregate, weights); @@ -456,8 +456,8 @@ public Long zInterStore(byte[] destKey, Aggregate aggregate, Weights weights, by Assert.notNull(destKey, "Destination key must not be null"); Assert.notNull(sets, "Source sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights %d must match the number of source sets %d".formatted(weights.size(), sets.length)); ZStoreArgs storeArgs = zStoreArgs(aggregate, weights); @@ -496,8 +496,8 @@ public Set zUnionWithScores(Aggregate aggregate, Weights weights, byte[]. Assert.notNull(sets, "Sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights %d must match the number of source sets %d".formatted(weights.size(), sets.length)); ZAggregateArgs zAggregateArgs = zAggregateArgs(aggregate, weights); @@ -511,8 +511,8 @@ public Long zUnionStore(byte[] destKey, Aggregate aggregate, Weights weights, by Assert.notNull(destKey, "Destination key must not be null"); Assert.notNull(sets, "Source sets must not be null"); Assert.noNullElements(sets, "Source sets must not contain null elements"); - Assert.isTrue(weights.size() == sets.length, () -> String - .format("The number of weights (%d) must match the number of source sets (%d)", weights.size(), sets.length)); + Assert.isTrue(weights.size() == sets.length, + "The number of weights %d must match the number of source sets %d".formatted(weights.size(), sets.length)); ZStoreArgs storeArgs = zStoreArgs(aggregate, weights); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProvider.java index ca3d4337b0..c77d4d077f 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProvider.java @@ -79,7 +79,7 @@ class StaticMasterReplicaConnectionProvider implements LettuceConnectionProvider return connectionType.cast(connection); } - throw new UnsupportedOperationException(String.format("Connection type %s not supported", connectionType)); + throw new UnsupportedOperationException("Connection type %s not supported".formatted(connectionType)); } @Override @@ -97,6 +97,6 @@ class StaticMasterReplicaConnectionProvider implements LettuceConnectionProvider }); } - throw new UnsupportedOperationException(String.format("Connection type %s not supported", connectionType)); + throw new UnsupportedOperationException("Connection type %s not supported".formatted(connectionType)); } } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java index 21c28a41ee..2f974056b0 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java @@ -217,7 +217,7 @@ public Span start(RedisCommand command) { this.command = command; if (log.isDebugEnabled()) { - log.debug(String.format("Starting Observation for Command %s", command)); + log.debug("Starting Observation for Command %s".formatted(command)); } if (command instanceof CompleteableCommand completeableCommand) { @@ -265,7 +265,7 @@ public Span tag(String key, String value) { public Span error(Throwable throwable) { if (log.isDebugEnabled()) { - log.debug(String.format("Attaching error to Observation for Command %s", command)); + log.debug("Attaching error to Observation for Command %s".formatted(command)); } observation.error(throwable); @@ -283,7 +283,7 @@ public Span remoteEndpoint(Endpoint endpoint) { public void finish() { if (log.isDebugEnabled()) { - log.debug(String.format("Stopping Observation for Command %s", command)); + log.debug("Stopping Observation for Command %s".formatted(command)); } observation.stop(); diff --git a/src/main/java/org/springframework/data/redis/connection/stream/Consumer.java b/src/main/java/org/springframework/data/redis/connection/stream/Consumer.java index 6219266652..0b43ba5933 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/Consumer.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/Consumer.java @@ -53,7 +53,7 @@ public static Consumer from(String group, String name) { @Override public String toString() { - return String.format("%s:%s", group, name); + return "%s:%s".formatted(group, name); } public String getGroup() { diff --git a/src/main/java/org/springframework/data/redis/connection/stream/StreamRecords.java b/src/main/java/org/springframework/data/redis/connection/stream/StreamRecords.java index bf9a34eec3..90de8d0d4e 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/StreamRecords.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/StreamRecords.java @@ -216,7 +216,7 @@ public ByteBufferRecord ofBuffer(Map value) { } else if (stream instanceof byte[]) { streamKey = ByteBuffer.wrap((byte[]) stream); } else { - throw new IllegalArgumentException(String.format("Stream key %s cannot be converted to byte buffer", stream)); + throw new IllegalArgumentException("Stream key %s cannot be converted to byte buffer".formatted(stream)); } return new ByteBufferMapBackedRecord(streamKey, id, value); diff --git a/src/main/java/org/springframework/data/redis/core/IndexWriter.java b/src/main/java/org/springframework/data/redis/core/IndexWriter.java index 3d3b048de5..c1060b08df 100644 --- a/src/main/java/org/springframework/data/redis/core/IndexWriter.java +++ b/src/main/java/org/springframework/data/redis/core/IndexWriter.java @@ -238,7 +238,7 @@ protected void addKeyToIndex(byte[] key, IndexedData indexedData) { connection.sAdd(createIndexKey(indexedData.getKeyspace(), key), indexKey); } else { throw new IllegalArgumentException( - String.format("Cannot write index data for unknown index type %s", indexedData.getClass())); + "Cannot write index data for unknown index type %s".formatted(indexedData.getClass())); } } @@ -292,10 +292,9 @@ private byte[] toBytes(@Nullable Object source) { return converter.getConversionService().convert(source, byte[].class); } - throw new InvalidDataAccessApiUsageException(String.format( - "Cannot convert %s to binary representation for index key generation; " - + "Are you missing a Converter; Did you register a non PathBasedRedisIndexDefinition that might apply to a complex type", - source.getClass())); + throw new InvalidDataAccessApiUsageException(("Cannot convert %s to binary representation for index key generation;" + + " Are you missing a Converter; Did you register a non PathBasedRedisIndexDefinition" + + " that might apply to a complex type").formatted(source.getClass())); } /** diff --git a/src/main/java/org/springframework/data/redis/core/RedisCommand.java b/src/main/java/org/springframework/data/redis/core/RedisCommand.java index 5621a216d9..1959d21128 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisCommand.java +++ b/src/main/java/org/springframework/data/redis/core/RedisCommand.java @@ -377,16 +377,17 @@ public void validateArgumentCount(int argumentCount) { if (requiresArguments()) { if (requiresExactNumberOfArguments()) { if (argumentCount != this.maxArgs) { - throw newIllegalArgumentException("%s command requires %d %s", name(), this.maxArgs, arguments(this.maxArgs)); + throw new IllegalArgumentException( + "%s command requires %d %s".formatted(name(), this.maxArgs, arguments(this.maxArgs))); } } if (argumentCount < this.minArgs) { - throw newIllegalArgumentException("%s command requires at least %d %s", name(), this.minArgs, - arguments(this.maxArgs)); + throw new IllegalArgumentException( + "%s command requires at least %d %s".formatted(name(), this.minArgs, arguments(this.maxArgs))); } if (this.maxArgs > 0 && argumentCount > this.maxArgs) { - throw newIllegalArgumentException("%s command requires at most %s %s", name(), this.maxArgs, - arguments(this.maxArgs)); + throw new IllegalArgumentException( + "%s command requires at most %s %s".formatted(name(), this.maxArgs, arguments(this.maxArgs))); } } } @@ -394,9 +395,4 @@ public void validateArgumentCount(int argumentCount) { private String arguments(int count) { return count == 1 ? "argument" : "arguments"; } - - private IllegalArgumentException newIllegalArgumentException(String message, Object... arguments) { - return new IllegalArgumentException(String.format(message, arguments)); - } - } diff --git a/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java b/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java index 20e4954ae0..767343557b 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java +++ b/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java @@ -495,14 +495,14 @@ public Object intercept(Object obj, Method method, Object[] args) throws Throwab if (isPotentiallyThreadBoundCommand(commandToExecute)) { if (log.isDebugEnabled()) { - log.debug(String.format("Invoke '%s' on bound connection", method.getName())); + log.debug("Invoke '%s' on bound connection".formatted(method.getName())); } return invoke(method, obj, args); } if (log.isDebugEnabled()) { - log.debug(String.format("Invoke '%s' on unbound connection", method.getName())); + log.debug("Invoke '%s' on unbound connection".formatted(method.getName())); } RedisConnection connection = factory.getConnection(); diff --git a/src/main/java/org/springframework/data/redis/core/convert/BinaryConverters.java b/src/main/java/org/springframework/data/redis/core/convert/BinaryConverters.java index 402a68d7ac..187c42e807 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/BinaryConverters.java +++ b/src/main/java/org/springframework/data/redis/core/convert/BinaryConverters.java @@ -289,7 +289,7 @@ public Date convert(byte[] source) { } catch (ParseException ignore) { } - throw new IllegalArgumentException(String.format("Cannot parse date out of %s", Arrays.toString(source))); + throw new IllegalArgumentException("Cannot parse date out of %s".formatted(Arrays.toString(source))); } } diff --git a/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java b/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java index e9e1a9f312..813c8c5a69 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java +++ b/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java @@ -540,8 +540,8 @@ private void writePartialPropertyUpdate(PartialUpdate update, PropertyUpdate map.put(((Entry) pUpdate.getValue()).getKey(), ((Entry) pUpdate.getValue()).getValue()); } else { throw new MappingException( - String.format("Cannot set update value for map property '%s' to '%s'; Please use a Map or Map.Entry", - pUpdate.getPropertyPath(), pUpdate.getValue())); + ("Cannot set update value for map property '%s' to '%s';" + " Please use a Map or Map.Entry") + .formatted(pUpdate.getPropertyPath(), pUpdate.getValue())); } writeMap(entity.getKeySpace(), pUpdate.getPropertyPath(), targetProperty.getMapValueType(), map, sink); @@ -601,8 +601,7 @@ private void writeInternal(@Nullable String keyspace, String path, @Nullable Obj } else { if (!ClassUtils.isAssignable(typeHint.getType(), value.getClass())) { - throw new MappingException( - String.format(INVALID_TYPE_ASSIGNMENT, value.getClass(), path, typeHint.getType())); + throw new MappingException(INVALID_TYPE_ASSIGNMENT.formatted(value.getClass(), path, typeHint.getType())); } writeToBucket(path, value, sink, typeHint.getType()); } @@ -751,7 +750,7 @@ private void writeCollection(@Nullable String keyspace, String path, @Nullable I if (!ClassUtils.isAssignable(typeHint.getType(), value.getClass())) { throw new MappingException( - String.format(INVALID_TYPE_ASSIGNMENT, value.getClass(), currentPath, typeHint.getType())); + INVALID_TYPE_ASSIGNMENT.formatted(value.getClass(), currentPath, typeHint.getType())); } if (customConversions.hasCustomWriteTarget(value.getClass())) { @@ -794,7 +793,7 @@ private void writeToBucket(String path, @Nullable Object value, RedisData sink, sink.getBucket().put(path, toBytes(value)); } else { throw new IllegalArgumentException( - String.format("Cannot convert value '%s' of type %s to bytes", value, value.getClass())); + "Cannot convert value '%s' of type %s to bytes".formatted(value, value.getClass())); } } } @@ -855,7 +854,7 @@ private void writeMap(@Nullable String keyspace, String path, Class mapValueT if (!ClassUtils.isAssignable(mapValueType, entry.getValue().getClass())) { throw new MappingException( - String.format(INVALID_TYPE_ASSIGNMENT, entry.getValue().getClass(), currentPath, mapValueType)); + INVALID_TYPE_ASSIGNMENT.formatted(entry.getValue().getClass(), currentPath, mapValueType)); } if (customConversions.hasCustomWriteTarget(entry.getValue().getClass())) { @@ -945,8 +944,7 @@ private Object extractMapKeyForPath(String path, String key, Class targetType Matcher matcher = pattern.matcher(key); if (!matcher.find()) { - throw new IllegalArgumentException( - String.format("Cannot extract map value for key '%s' in path '%s'.", key, path)); + throw new IllegalArgumentException("Cannot extract map value for key '%s' in path '%s'".formatted(key, path)); } Object mapKey = matcher.group(2); @@ -1224,7 +1222,7 @@ private KeyspaceIdentifier(String keyspace, String id, boolean phantomKey) { */ public static KeyspaceIdentifier of(String key) { - Assert.isTrue(isValid(key), String.format("Invalid key %s", key)); + Assert.isTrue(isValid(key), () -> "Invalid key %s".formatted(key)); boolean phantomKey = key.endsWith(PHANTOM_SUFFIX); int keyspaceEndIndex = key.indexOf(DELIMITER); @@ -1304,7 +1302,7 @@ private BinaryKeyspaceIdentifier(byte[] keyspace, byte[] id, boolean phantomKey) */ public static BinaryKeyspaceIdentifier of(byte[] key) { - Assert.isTrue(isValid(key), String.format("Invalid key %s", new String(key))); + Assert.isTrue(isValid(key), () -> "Invalid key %s".formatted(new String(key))); boolean phantomKey = ByteUtils.startsWith(key, PHANTOM_SUFFIX, key.length - PHANTOM_SUFFIX.length); diff --git a/src/main/java/org/springframework/data/redis/core/index/GeoIndexDefinition.java b/src/main/java/org/springframework/data/redis/core/index/GeoIndexDefinition.java index 00a7edc640..94157fef78 100644 --- a/src/main/java/org/springframework/data/redis/core/index/GeoIndexDefinition.java +++ b/src/main/java/org/springframework/data/redis/core/index/GeoIndexDefinition.java @@ -66,8 +66,8 @@ public Point convert(@Nullable Object source) { } throw new IllegalArgumentException( - String.format("Cannot convert %s to %s; GeoIndexed property needs to be of type Point or GeoLocation", - source.getClass(), Point.class)); + ("Cannot convert %s to %s; GeoIndexed property needs to be of type Point" + " or GeoLocation") + .formatted(source.getClass(), Point.class)); } } } diff --git a/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java b/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java index 14bbc8ee6a..5b2f36333b 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java @@ -84,16 +84,14 @@ protected RedisPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNul boolean newIdPropertyIsExplicit = property.isAnnotationPresent(Id.class); if (currentIdPropertyIsExplicit && newIdPropertyIsExplicit) { - throw new MappingException(String.format( - "Attempt to add explicit id property %s but already have an property %s registered " - + "as explicit id; Check your mapping configuration", - property.getField(), currentIdProperty.getField())); + throw new MappingException(("Attempt to add explicit id property %s but already have a property %s" + + " registered as explicit id; Check your mapping configuration") + .formatted(property.getField(), currentIdProperty.getField())); } if (!currentIdPropertyIsExplicit && !newIdPropertyIsExplicit) { - throw new MappingException( - String.format("Attempt to add id property %s but already have an property %s registered " - + "as id; Check your mapping configuration", property.getField(), currentIdProperty.getField())); + throw new MappingException(("Attempt to add id property %s but already have a property %s registered as id;" + + " Check your mapping configuration").formatted(property.getField(), currentIdProperty.getField())); } if (newIdPropertyIsExplicit) { diff --git a/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java b/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java index e623311ee9..0907cab4f2 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java @@ -241,17 +241,15 @@ public Long getTimeToLive(Object source) { return TimeUnit.SECONDS.convert(timeout.longValue(), ttl.unit()); } } catch (IllegalAccessException ex) { - String message = String.format("Not allowed to access method '%s': %s", - timeoutMethod.getName(), ex.getMessage()); - throw new IllegalStateException(message, ex); + throw new IllegalStateException( + "Not allowed to access method '%s': %s".formatted(timeoutMethod.getName(), ex.getMessage()), ex); } catch (IllegalArgumentException ex) { - String message = String.format("Cannot invoke method '%s' without arguments: %s", - timeoutMethod.getName(), ex.getMessage()); - throw new IllegalStateException(message, ex); + throw new IllegalStateException( + "Cannot invoke method '%s' without arguments: %s".formatted(timeoutMethod.getName(), ex.getMessage()), + ex); } catch (InvocationTargetException ex) { - String message = String.format("Cannot access method '%s': %s", - timeoutMethod.getName(), ex.getMessage()); - throw new IllegalStateException(message, ex); + throw new IllegalStateException( + "Cannot access method '%s': %s".formatted(timeoutMethod.getName(), ex.getMessage()), ex); } } } diff --git a/src/main/java/org/springframework/data/redis/core/script/DigestUtils.java b/src/main/java/org/springframework/data/redis/core/script/DigestUtils.java index d8b4635ca3..316fa5ea6f 100644 --- a/src/main/java/org/springframework/data/redis/core/script/DigestUtils.java +++ b/src/main/java/org/springframework/data/redis/core/script/DigestUtils.java @@ -60,8 +60,7 @@ private static MessageDigest getDigest(String algorithm) { try { return MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException ex) { - String message = String.format("Could not find MessageDigest with algorithm \"%s\"", algorithm); - throw new IllegalStateException(message, ex); + throw new IllegalStateException("MessageDigest with algorithm '%s' not found".formatted(algorithm), ex); } } } diff --git a/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java b/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java index 3db864730f..95a511f99d 100644 --- a/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java +++ b/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java @@ -274,8 +274,7 @@ public static RedisClientInfo fromString(String source) { try { properties.load(new StringReader(source.replace(' ', '\n'))); } catch (IOException ex) { - String message = String.format("Properties could not be loaded from String '%s'", source); - throw new IllegalArgumentException(message, ex); + throw new IllegalArgumentException("Properties could not be loaded from String '%s'".formatted(source), ex); } return new RedisClientInfo(properties); } diff --git a/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java b/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java index 3411917290..b6702bcc24 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java @@ -107,6 +107,6 @@ public boolean equals(@Nullable Object o) { @Override public String toString() { - return String.format("Bounding box: [width=%s, height=%s]", width, height); + return "Bounding box: [width=%s, height=%s]".formatted(width, height); } } diff --git a/src/main/java/org/springframework/data/redis/hash/BeanUtilsHashMapper.java b/src/main/java/org/springframework/data/redis/hash/BeanUtilsHashMapper.java index ad85827105..bdb4a29671 100644 --- a/src/main/java/org/springframework/data/redis/hash/BeanUtilsHashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/BeanUtilsHashMapper.java @@ -71,7 +71,7 @@ public Map toHash(T object) { return result; } catch (Exception ex) { - throw new IllegalArgumentException(String.format("Cannot describe object %s", object), ex); + throw new IllegalArgumentException("Cannot describe object %s".formatted(object), ex); } } } diff --git a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java index 7b5ca20543..b1eb838734 100644 --- a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java @@ -412,8 +412,7 @@ private void flattenElement(String propertyPrefix, Object source, Map t Collection collection = resolveMessageListeners(this.channelMapping, serializedTopic); collection.add(listener); channels.add(serializedTopic.getArray()); - logTrace(() -> String.format("Adding listener '%s' on channel '%s'", listener, topic.getTopic())); + logTrace(() -> "Adding listener '%s' on channel '%s'".formatted(listener, topic.getTopic())); } else if (topic instanceof PatternTopic) { Collection collection = resolveMessageListeners(this.patternMapping, serializedTopic); collection.add(listener); patterns.add(serializedTopic.getArray()); - logTrace(() -> String.format("Adding listener '%s' for pattern '%s'", listener, topic.getTopic())); + logTrace(() -> "Adding listener '%s' for pattern '%s'".formatted(listener, topic.getTopic())); } else { - throw new IllegalArgumentException(String.format("Unknown topic type '%s'", topic.getClass())); + throw new IllegalArgumentException("Unknown topic type '%s'".formatted(topic.getClass())); } } boolean wasListening = isListening(); @@ -748,12 +748,12 @@ private void removeListener(@Nullable MessageListener listener, Collection String.format("Removing listener '%s' from channel '%s'", listener, topic.getTopic())); + logTrace(() -> "Removing listener '%s' from channel '%s'".formatted(listener, topic.getTopic())); } else if (topic instanceof PatternTopic) { remove(listener, topic, holder, patternMapping, patternsToRemove); - logTrace(() -> String.format("Removing listener '%s' from pattern '%s'", listener, topic.getTopic())); + logTrace(() -> "Removing listener '%s' from pattern '%s'".formatted(listener, topic.getTopic())); } } @@ -874,9 +874,8 @@ protected void handleSubscriptionException(CompletableFuture future, BackO long recoveryInterval = backOffExecution.nextBackOff(); if (recoveryInterval != BackOffExecution.STOP) { - String message = String.format("Connection failure occurred: %s; Restarting subscription task after %s ms", - cause, recoveryInterval); - logger.error(message, cause); + logger.error("Connection failure occurred: %s; Restarting subscription task after %s ms".formatted(cause, + recoveryInterval), cause); } return recoveryInterval; diff --git a/src/main/java/org/springframework/data/redis/listener/adapter/MessageListenerAdapter.java b/src/main/java/org/springframework/data/redis/listener/adapter/MessageListenerAdapter.java index 2152b4fad9..ed9c67ae2b 100644 --- a/src/main/java/org/springframework/data/redis/listener/adapter/MessageListenerAdapter.java +++ b/src/main/java/org/springframework/data/redis/listener/adapter/MessageListenerAdapter.java @@ -375,13 +375,12 @@ protected void invokeListenerMethod(String methodName, Object[] arguments) { if (targetEx instanceof DataAccessException dataAccessException) { throw dataAccessException; } else { - String message = String.format("Listener method '%s' threw exception", methodName); - throw new RedisListenerExecutionFailedException(message, targetEx); + throw new RedisListenerExecutionFailedException("Listener method '%s' threw exception".formatted(methodName), + targetEx); } } catch (Throwable ex) { - String message = String.format("Failed to invoke target method '%s' with arguments %s", methodName, - ObjectUtils.nullSafeToString(arguments)); - throw new RedisListenerExecutionFailedException(message, ex); + throw new RedisListenerExecutionFailedException("Failed to invoke target method '%s' with arguments %s" + .formatted(methodName, ObjectUtils.nullSafeToString(arguments)), ex); } } diff --git a/src/main/java/org/springframework/data/redis/repository/cdi/CdiBean.java b/src/main/java/org/springframework/data/redis/repository/cdi/CdiBean.java index bdb9248f6f..6a12dd3ea6 100644 --- a/src/main/java/org/springframework/data/redis/repository/cdi/CdiBean.java +++ b/src/main/java/org/springframework/data/redis/repository/cdi/CdiBean.java @@ -149,8 +149,7 @@ public final void initialize() { public void destroy(T instance, CreationalContext creationalContext) { if (log.isDebugEnabled()) { - log.debug(String.format("Destroying bean instance %s for repository type '%s'.", instance.toString(), - beanClass.getName())); + log.debug("Destroying bean instance %s for repository type '%s'".formatted(instance, beanClass.getName())); } creationalContext.release(); @@ -206,7 +205,7 @@ public String getId() { @Override public String toString() { - return String.format("CdiBean: type='%s', qualifiers=%s", beanClass.getName(), qualifiers.toString()); + return "CdiBean: type='%s', qualifiers=%s".formatted(beanClass.getName(), qualifiers.toString()); } } diff --git a/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryExtension.java b/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryExtension.java index c445925f6e..63d88805de 100644 --- a/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryExtension.java +++ b/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryExtension.java @@ -80,7 +80,7 @@ void processBean(@Observes ProcessBean processBean) { if (beanType instanceof Class && RedisKeyValueTemplate.class.isAssignableFrom((Class) beanType)) { if (log.isDebugEnabled()) { - log.debug(String.format("Discovered %s with qualifiers %s.", RedisKeyValueTemplate.class.getName(), + log.debug("Discovered %s with qualifiers %s.".formatted(RedisKeyValueTemplate.class.getName(), bean.getQualifiers())); } @@ -90,7 +90,7 @@ void processBean(@Observes ProcessBean processBean) { if (beanType instanceof Class && RedisKeyValueAdapter.class.isAssignableFrom((Class) beanType)) { if (log.isDebugEnabled()) { - log.debug(String.format("Discovered %s with qualifiers %s.", RedisKeyValueAdapter.class.getName(), + log.debug("Discovered %s with qualifiers %s.".formatted(RedisKeyValueAdapter.class.getName(), bean.getQualifiers())); } @@ -100,7 +100,8 @@ void processBean(@Observes ProcessBean processBean) { if (beanType instanceof Class && RedisOperations.class.isAssignableFrom((Class) beanType)) { if (log.isDebugEnabled()) { - log.debug(String.format("Discovered %s with qualifiers %s.", RedisOperations.class.getName(), + log.debug( + "Discovered %s with qualifiers %s.".formatted(RedisOperations.class.getName(), bean.getQualifiers())); } @@ -123,7 +124,7 @@ void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanMan CdiRepositoryBean repositoryBean = createRepositoryBean(repositoryType, qualifiers, beanManager); if (log.isInfoEnabled()) { - log.info(String.format("Registering bean for %s with qualifiers %s.", repositoryType.getName(), qualifiers)); + log.info("Registering bean for %s with qualifiers %s.".formatted(repositoryType.getName(), qualifiers)); } // Register the bean to the container. @@ -148,7 +149,7 @@ private void registerDependenciesIfNecessary(@Observes AfterBeanDiscovery afterB if (!redisKeyValueAdapters.containsKey(qualifiers)) { if (log.isInfoEnabled()) { - log.info(String.format("Registering bean for %s with qualifiers %s.", RedisKeyValueAdapter.class.getName(), + log.info("Registering bean for %s with qualifiers %s.".formatted(RedisKeyValueAdapter.class.getName(), qualifiers)); } RedisKeyValueAdapterBean redisKeyValueAdapterBean = createRedisKeyValueAdapterBean(qualifiers, beanManager); @@ -158,7 +159,7 @@ private void registerDependenciesIfNecessary(@Observes AfterBeanDiscovery afterB if (!redisKeyValueTemplates.containsKey(qualifiers)) { if (log.isInfoEnabled()) { - log.info(String.format("Registering bean for %s with qualifiers %s.", RedisKeyValueTemplate.class.getName(), + log.info("Registering bean for %s with qualifiers %s.".formatted(RedisKeyValueTemplate.class.getName(), qualifiers)); } @@ -186,8 +187,8 @@ private CdiRepositoryBean createRepositoryBean(Class repositoryType, S Bean redisKeyValueTemplate = this.redisKeyValueTemplates.get(qualifiers); if (redisKeyValueTemplate == null) { - throw new UnsatisfiedResolutionException(String.format("Unable to resolve a bean for '%s' with qualifiers %s.", - RedisKeyValueTemplate.class.getName(), qualifiers)); + throw new UnsatisfiedResolutionException("Unable to resolve a bean for '%s' with qualifiers %s" + .formatted(RedisKeyValueTemplate.class.getName(), qualifiers)); } // Construct and return the repository bean. @@ -208,8 +209,8 @@ private RedisKeyValueAdapterBean createRedisKeyValueAdapterBean(Set Bean> redisOperationsBean = this.redisOperations.get(qualifiers); if (redisOperationsBean == null) { - throw new UnsatisfiedResolutionException(String.format("Unable to resolve a bean for '%s' with qualifiers %s.", - RedisOperations.class.getName(), qualifiers)); + throw new UnsatisfiedResolutionException("Unable to resolve a bean for '%s' with qualifiers %s." + .formatted(RedisOperations.class.getName(), qualifiers)); } // Construct and return the repository bean. @@ -230,8 +231,8 @@ private RedisKeyValueTemplateBean createRedisKeyValueTemplateBean(Set redisKeyValueAdapterBean = this.redisKeyValueAdapters.get(qualifiers); if (redisKeyValueAdapterBean == null) { - throw new UnsatisfiedResolutionException(String.format("Unable to resolve a bean for '%s' with qualifiers %s.", - RedisKeyValueAdapter.class.getName(), qualifiers)); + throw new UnsatisfiedResolutionException("Unable to resolve a bean for '%s' with qualifiers %s" + .formatted(RedisKeyValueAdapter.class.getName(), qualifiers)); } // Construct and return the repository bean. diff --git a/src/main/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformation.java b/src/main/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformation.java index c05d61f12c..3af170b234 100644 --- a/src/main/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformation.java +++ b/src/main/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformation.java @@ -40,10 +40,9 @@ public MappingRedisEntityInformation(RedisPersistentEntity entity) { super(entity); if (!entity.hasIdProperty()) { - throw new MappingException( - String.format("Entity %s requires to have an explicit id field; Did you forget to provide one using @Id", - entity.getName())); + ("Entity %s requires to have an explicit id field;" + " Did you forget to provide one using @Id") + .formatted(entity.getName())); } } } diff --git a/src/main/java/org/springframework/data/redis/repository/query/ExampleQueryMapper.java b/src/main/java/org/springframework/data/redis/repository/query/ExampleQueryMapper.java index fe82d0327e..a5b0c76443 100644 --- a/src/main/java/org/springframework/data/redis/repository/query/ExampleQueryMapper.java +++ b/src/main/java/org/springframework/data/redis/repository/query/ExampleQueryMapper.java @@ -139,8 +139,8 @@ private void applyPropertySpec(String path, Predicate hasIndex, ExampleM if (!SUPPORTED_MATCHERS.contains(stringMatcher)) { throw new InvalidDataAccessApiUsageException( - String.format("Redis Query-by-Example does not support string matcher %s; Supported matchers are: %s.", - stringMatcher, SUPPORTED_MATCHERS)); + ("Redis Query-by-Example does not support string matcher %s;" + " Supported matchers are: %s.") + .formatted(stringMatcher, SUPPORTED_MATCHERS)); } if (exampleSpecAccessor.hasPropertySpecifier(path)) { diff --git a/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java b/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java index 0479654a88..c7541ac8e7 100644 --- a/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java +++ b/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java @@ -61,10 +61,8 @@ private RedisOperationChain from(Part part, Iterator iterator, RedisOper case TRUE -> sink.sismember(part.getProperty().toDotPath(), true); case FALSE -> sink.sismember(part.getProperty().toDotPath(), false); case WITHIN, NEAR -> sink.near(getNearPath(part, iterator)); - default -> { - String message = String.format("%s is not supported for Redis query derivation", part.getType()); - throw new IllegalArgumentException(message); - } + default -> + throw new IllegalArgumentException("%s is not supported for Redis query derivation".formatted(part.getType())); } return sink; @@ -111,8 +109,8 @@ private NearPath getNearPath(Part part, Iterator iterator) { if (value instanceof Point point) { if (!iterator.hasNext()) { - String message = "Expected to find distance value for geo query; Are you missing a parameter?"; - throw new InvalidDataAccessApiUsageException(message); + throw new InvalidDataAccessApiUsageException( + "Expected to find distance value for geo query;" + " Are you missing a parameter?"); } Distance distance; @@ -124,17 +122,16 @@ private NearPath getNearPath(Part part, Iterator iterator) { distance = new Distance(num.doubleValue(), Metrics.KILOMETERS); } else { - String message = String.format("Expected to find Distance or Numeric value for geo query but was %s", - ClassUtils.getDescriptiveType(distObject)); - throw new InvalidDataAccessApiUsageException(message); + throw new InvalidDataAccessApiUsageException( + "Expected to find Distance or Numeric value for geo query but was %s" + .formatted(ClassUtils.getDescriptiveType(distObject))); } return new NearPath(path, point, distance); } - String message = String.format("Expected to find a Circle or Point/Distance for geo query but was %s", - ClassUtils.getDescriptiveType(value.getClass())); - throw new InvalidDataAccessApiUsageException(message); + throw new InvalidDataAccessApiUsageException("Expected to find a Circle or Point/Distance for geo query but was %s" + .formatted(ClassUtils.getDescriptiveType(value.getClass()))); } private static boolean containsExactlyOne(Collection collection) { diff --git a/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementWriter.java b/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementWriter.java index 43fa91fa2d..38bfc19249 100644 --- a/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementWriter.java +++ b/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementWriter.java @@ -18,6 +18,7 @@ import java.nio.ByteBuffer; import org.springframework.lang.Nullable; +import org.springframework.util.ObjectUtils; /** * Default implementation of {@link RedisElementWriter}. @@ -38,7 +39,8 @@ class DefaultRedisElementWriter implements RedisElementWriter { public ByteBuffer write(@Nullable T value) { if (serializer != null && (value == null || serializer.canSerialize(value.getClass()))) { - return ByteBuffer.wrap(serializer.serialize(value)); + byte[] serializedValue = serializer.serialize(value); + return serializedValue != null ? ByteBuffer.wrap(serializedValue) : ByteBuffer.wrap(new byte[0]); } if (value instanceof byte[]) { @@ -50,6 +52,6 @@ public ByteBuffer write(@Nullable T value) { } throw new IllegalStateException( - String.format("Cannot serialize value of type %s without a serializer", value.getClass())); + "Cannot serialize value of type %s without a serializer".formatted(ObjectUtils.nullSafeClassName(value))); } } diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index ae968d6141..7ecc89d18c 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -266,8 +266,7 @@ public byte[] serialize(@Nullable Object value) throws SerializationException { try { return writer.write(mapper, value); } catch (IOException ex) { - String message = String.format("Could not write JSON: %s", ex.getMessage()); - throw new SerializationException(message, ex); + throw new SerializationException("Could not write JSON: %s".formatted(ex.getMessage()), ex); } } @@ -303,8 +302,7 @@ public T deserialize(@Nullable byte[] source, Class type) throws Serializ try { return (T) reader.read(mapper, source, resolveType(source, type)); } catch (Exception ex) { - String message = String.format("Could not read JSON:%s ", ex.getMessage()); - throw new SerializationException(message, ex); + throw new SerializationException("Could not read JSON:%s ".formatted(ex.getMessage()), ex); } } diff --git a/src/main/java/org/springframework/data/redis/stream/DefaultStreamReceiver.java b/src/main/java/org/springframework/data/redis/stream/DefaultStreamReceiver.java index 02af99b523..7452abfa21 100644 --- a/src/main/java/org/springframework/data/redis/stream/DefaultStreamReceiver.java +++ b/src/main/java/org/springframework/data/redis/stream/DefaultStreamReceiver.java @@ -105,7 +105,7 @@ class DefaultStreamReceiver> implements StreamReceiver public Flux receive(StreamOffset streamOffset) { if (logger.isDebugEnabled()) { - logger.debug(String.format("receive(%s)", streamOffset)); + logger.debug("receive(%s)".formatted(streamOffset)); } RedisSerializationContext.SerializationPair keySerializer = template.getSerializationContext() @@ -125,11 +125,10 @@ public Flux receive(StreamOffset streamOffset) { } @Override - @SuppressWarnings("unchecked") public Flux receiveAutoAck(Consumer consumer, StreamOffset streamOffset) { if (logger.isDebugEnabled()) { - logger.debug(String.format("receiveAutoAck(%s, %s)", consumer, streamOffset)); + logger.debug("receiveAutoAck(%s, %s)".formatted(consumer, streamOffset)); } Function> readFunction = getConsumeReadFunction(streamOffset.getKey(), consumer, @@ -146,11 +145,10 @@ public Flux receiveAutoAck(Consumer consumer, StreamOffset streamOffset) { } @Override - @SuppressWarnings("unchecked") public Flux receive(Consumer consumer, StreamOffset streamOffset) { if (logger.isDebugEnabled()) { - logger.debug(String.format("receive(%s, %s)", consumer, streamOffset)); + logger.debug("receive(%s, %s)".formatted(consumer, streamOffset)); } Function> readFunction = getConsumeReadFunction(streamOffset.getKey(), consumer, @@ -229,7 +227,7 @@ void arm() { sink.onRequest(toAdd -> { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] onRequest(%d)", key, toAdd)); + logger.debug("[stream: %s] onRequest(%d)".formatted(key, toAdd)); } if (pollState.isSubscriptionActive()) { @@ -251,7 +249,7 @@ void arm() { } } else { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] onRequest(%d): Dropping, subscription canceled", key, toAdd)); + logger.debug("[stream: %s] onRequest(%d): Dropping, subscription canceled".formatted(key, toAdd)); } } }); @@ -263,25 +261,25 @@ void arm() { private void scheduleIfRequired() { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] scheduleIfRequired()", key)); + logger.debug("[stream: %s] scheduleIfRequired()".formatted(key)); } if (pollState.isScheduled()) { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] scheduleIfRequired(): Already scheduled", key)); + logger.debug("[stream: %s] scheduleIfRequired(): Already scheduled".formatted(key)); } return; } if (!pollState.isSubscriptionActive()) { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] scheduleIfRequired(): Subscription cancelled", key)); + logger.debug("[stream: %s] scheduleIfRequired(): Subscription cancelled".formatted(key)); } return; } if (pollState.getRequested() > 0 && !overflow.isEmpty()) { if (logger.isDebugEnabled()) { - logger.info(String.format("[stream: %s] scheduleIfRequired(): Requested: %d Emit from buffer", key, + logger.info("[stream: %s] scheduleIfRequired(): Requested: %d Emit from buffer".formatted(key, pollState.getRequested())); } emitBuffer(); @@ -290,8 +288,8 @@ private void scheduleIfRequired() { if (pollState.getRequested() == 0) { if (logger.isDebugEnabled()) { - logger.debug(String - .format("[stream: %s] scheduleIfRequired(): Subscriber has no demand; Suspending subscription", key)); + logger.debug( + "[stream: %s] scheduleIfRequired(): Subscriber has no demand; Suspending subscription".formatted(key)); } return; } @@ -303,14 +301,14 @@ private void scheduleIfRequired() { if (pollState.activateSchedule()) { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] scheduleIfRequired(): Activating subscription", key)); + logger.debug("[stream: %s] scheduleIfRequired(): Activating subscription".formatted(key)); } ReadOffset readOffset = pollState.getCurrentReadOffset(); if (logger.isDebugEnabled()) { logger.debug( - String.format("[stream: %s] scheduleIfRequired(): Activating subscription, offset %s", key, readOffset)); + "[stream: %s] scheduleIfRequired(): Activating subscription, offset %s".formatted(key, readOffset)); } Flux poll = readFunction.apply(readOffset) @@ -319,7 +317,7 @@ private void scheduleIfRequired() { poll.map(it -> { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] onStreamMessage(%s)", key, it)); + logger.debug("[stream: %s] onStreamMessage(%s)".formatted(key, it)); } pollState.updateReadOffset(it.getId().getValue()); @@ -357,7 +355,7 @@ public void onError(Throwable t) { public void onComplete() { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] onComplete()", key)); + logger.debug("[stream: %s] onComplete()".formatted(key)); } pollState.scheduleCompleted(); @@ -381,20 +379,20 @@ private void onStreamMessage(V message) { if (requested == Long.MAX_VALUE) { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] onStreamMessage(%s): Emitting item, fast-path", key, message)); + logger.debug("[stream: %s] onStreamMessage(%s): Emitting item, fast-path".formatted(key, message)); } sink.next(message); } else { if (pollState.decrementRequested()) { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] onStreamMessage(%s): Emitting item, slow-path", key, message)); + logger.debug("[stream: %s] onStreamMessage(%s): Emitting item, slow-path".formatted(key, message)); } sink.next(message); } else { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] onStreamMessage(%s): Buffering overflow", key, message)); + logger.debug("[stream: %s] onStreamMessage(%s): Buffering overflow".formatted(key, message)); } overflow.offer(message); } @@ -403,7 +401,7 @@ private void onStreamMessage(V message) { } else { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] onStreamMessage(%s): Buffering overflow", key, message)); + logger.debug("[stream: %s] onStreamMessage(%s): Buffering overflow".formatted(key, message)); } overflow.offer(message); } @@ -412,7 +410,7 @@ private void onStreamMessage(V message) { private void onStreamError(Throwable t) { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] onStreamError(%s)", key, t)); + logger.debug("[stream: %s] onStreamError(%s)".formatted(key, t)); } pollState.cancel(); @@ -435,14 +433,13 @@ private void emitBuffer() { if (message == null) { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] emitBuffer(): emission missed", key)); + logger.debug("[stream: %s] emitBuffer(): emission missed".formatted(key)); } break; } if (logger.isDebugEnabled()) { - logger.debug( - String.format("[stream: %s] emitBuffer(%s): Emitting item from buffer, fast-path", key, message)); + logger.debug("[stream: %s] emitBuffer(%s): Emitting item from buffer, fast-path".formatted(key, message)); } sink.next(message); @@ -454,15 +451,14 @@ private void emitBuffer() { if (message == null) { if (logger.isDebugEnabled()) { - logger.debug(String.format("[stream: %s] emitBuffer(): emission missed", key)); + logger.debug("[stream: %s] emitBuffer(): emission missed".formatted(key)); } pollState.incrementRequested(); break; } if (logger.isDebugEnabled()) { - logger.debug( - String.format("[stream: %s] emitBuffer(%s): Emitting item from buffer, slow-path", key, message)); + logger.debug("[stream: %s] emitBuffer(%s): Emitting item from buffer, slow-path".formatted(key, message)); } sink.next(message); diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java index eb6c930145..9c1f70df7e 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java @@ -160,11 +160,12 @@ private void initializeIfAbsent() { public double get() { Double value = operations.get(key); + if (value != null) { return value; } - throw new DataRetrievalFailureException(String.format("The key '%s' seems to no longer exist", key)); + throw new DataRetrievalFailureException("The key '%s' seems to no longer exist".formatted(key)); } /** diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java index 04f43f8d73..f8e092c0bc 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java @@ -160,11 +160,12 @@ private void initializeIfAbsent() { public int get() { Integer value = operations.get(key); + if (value != null) { return value; } - throw new DataRetrievalFailureException(String.format("The key '%s' seems to no longer exist", key)); + throw new DataRetrievalFailureException("The key '%s' seems to no longer exist".formatted(key)); } /** diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java index f661d5dc3d..597b126278 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java @@ -161,11 +161,12 @@ private void initializeIfAbsent() { public long get() { Long value = operations.get(key); + if (value != null) { return value; } - throw new DataRetrievalFailureException(String.format("The key '%s' seems to no longer exist", key)); + throw new DataRetrievalFailureException("The key '%s' seems to no longer exist".formatted(key)); } /** diff --git a/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java b/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java index d8a2af95a9..842905a305 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java +++ b/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java @@ -166,13 +166,6 @@ public int hashCode() { @Override public String toString() { - - StringBuilder sb = new StringBuilder(); - - sb.append(String.format("%s for key:", getClass().getSimpleName())); - sb.append(getKey()); - - return sb.toString(); + return "%s for key: %s".formatted(getClass().getSimpleName(), getKey()); } - } diff --git a/src/main/java/org/springframework/data/redis/support/collections/ReversedRedisListView.java b/src/main/java/org/springframework/data/redis/support/collections/ReversedRedisListView.java index 8451a80dde..581a9920d2 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/ReversedRedisListView.java +++ b/src/main/java/org/springframework/data/redis/support/collections/ReversedRedisListView.java @@ -639,8 +639,9 @@ class DescendingListIterator implements ListIterator { DescendingListIterator(int size, int position) { if (position < 0 || position > size) { - String message = String.format("Position [%d] is out of bounds: [0, %d]", position, size); - throw new IndexOutOfBoundsException(message); + throw new IndexOutOfBoundsException( + ("Position [%d] is out of bounds;" + " position must be greater than equal to 0 and less than size %d") + .formatted(position, size)); } this.it = base.listIterator(size - position); diff --git a/src/main/java/org/springframework/data/redis/util/RedisAssertions.java b/src/main/java/org/springframework/data/redis/util/RedisAssertions.java index 34e5bfede7..2b0272de24 100644 --- a/src/main/java/org/springframework/data/redis/util/RedisAssertions.java +++ b/src/main/java/org/springframework/data/redis/util/RedisAssertions.java @@ -40,7 +40,7 @@ public abstract class RedisAssertions { * @see #requireNonNull(Object, Supplier) */ public static T requireNonNull(@Nullable T target, String message, Object... arguments) { - return requireNonNull(target, () -> String.format(message, arguments)); + return requireNonNull(target, () -> message.formatted(arguments)); } /** @@ -88,7 +88,7 @@ public static T requireNonNull(@Nullable T target, RuntimeExceptionSupplier * @see #requireNonNull(Object, Supplier) */ public static T requireState(@Nullable T target, String message, Object... arguments) { - return requireState(target, () -> String.format(message, arguments)); + return requireState(target, () -> message.formatted(arguments)); } /** diff --git a/src/test/java/org/springframework/data/redis/SettingsUtils.java b/src/test/java/org/springframework/data/redis/SettingsUtils.java index cadeca6ff7..107c61889f 100644 --- a/src/test/java/org/springframework/data/redis/SettingsUtils.java +++ b/src/test/java/org/springframework/data/redis/SettingsUtils.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Properties; import org.springframework.data.redis.connection.RedisClusterConfiguration; @@ -114,9 +115,11 @@ public static RedisStandaloneConfiguration standaloneConfiguration() { * @return a new {@link RedisSentinelConfiguration} initialized with test endpoint settings. */ public static RedisSentinelConfiguration sentinelConfiguration() { - return new RedisSentinelConfiguration(getSentinelMaster(), - new HashSet<>(Arrays.asList(String.format("%s:%d", getHost(), getSentinelPort()), - String.format("%s:%d", getHost(), getSentinelPort() + 1)))); + + List sentinelHostPorts = List.of("%s:%d".formatted(getHost(), getSentinelPort()), + "%s:%d".formatted(getHost(), getSentinelPort() + 1)); + + return new RedisSentinelConfiguration(getSentinelMaster(), new HashSet<>(sentinelHostPorts)); } /** @@ -125,8 +128,7 @@ public static RedisSentinelConfiguration sentinelConfiguration() { * @return a new {@link RedisClusterConfiguration} initialized with test endpoint settings. */ public static RedisClusterConfiguration clusterConfiguration() { - return new RedisClusterConfiguration( - Collections.singletonList(String.format("%s:%d", getHost(), getClusterPort()))); + return new RedisClusterConfiguration(List.of("%s:%d".formatted(getHost(), getClusterPort()))); } /** diff --git a/src/test/java/org/springframework/data/redis/connection/ClusterSlotHashUtilsTests.java b/src/test/java/org/springframework/data/redis/connection/ClusterSlotHashUtilsTests.java index f48d44ce8d..ed0f1ef8dc 100644 --- a/src/test/java/org/springframework/data/redis/connection/ClusterSlotHashUtilsTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ClusterSlotHashUtilsTests.java @@ -60,7 +60,7 @@ void localCalculationShouldMatchServers() { Long serverSlot = jedis.clusterKeySlot(key); assertThat(slot) - .as(String.format("Expected slot for key '%s' to be %s but server calculated %s.", key, slot, serverSlot)) + .describedAs("Expected slot for key '%s' to be %s but server calculated %s", key, slot, serverSlot) .isEqualTo(serverSlot.intValue()); } diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionIntegrationTests.java index 21446b5644..119b507663 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionIntegrationTests.java @@ -404,8 +404,8 @@ void pExpireShouldSupportExiprationForValuesLargerThanInteger() { long ttl = connection.pTtl("pexpireKey"); assertThat(millis - ttl < 20L) - .as(String.format("difference between millis=%s and ttl=%s should not be greater than 20ms but is %s", millis, - ttl, millis - ttl)) + .describedAs("difference between millis=%s and ttl=%s should not be greater than 20ms but is %s", + millis, ttl, millis - ttl) .isTrue(); } diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java index 3c91253f23..81a811a034 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java @@ -402,7 +402,7 @@ void connectsThroughRedisSocket() { @Test // DATAREDIS-762, DATAREDIS-869 void factoryUsesElastiCacheMasterReplicaConnections() { - assumeTrue(String.format("No replicas connected to %s:%s.", SettingsUtils.getHost(), SettingsUtils.getPort()), + assumeTrue("No replicas connected to %s:%d".formatted(SettingsUtils.getHost(), SettingsUtils.getPort()), connection.info("replication").getProperty("connected_slaves", "0").compareTo("0") > 0); LettuceClientConfiguration configuration = LettuceTestClientConfiguration.builder().readFrom(ReadFrom.REPLICA) @@ -429,7 +429,7 @@ void factoryUsesElastiCacheMasterReplicaConnections() { @Test // DATAREDIS-1093 void pubSubDoesNotSupportMasterReplicaConnections() { - assumeTrue(String.format("No replicas connected to %s:%s.", SettingsUtils.getHost(), SettingsUtils.getPort()), + assumeTrue("No replicas connected to %s:%d".formatted(SettingsUtils.getHost(), SettingsUtils.getPort()), connection.info("replication").getProperty("connected_slaves", "0").compareTo("0") > 0); RedisStaticMasterReplicaConfiguration elastiCache = new RedisStaticMasterReplicaConfiguration( @@ -451,7 +451,7 @@ void pubSubDoesNotSupportMasterReplicaConnections() { @Test // DATAREDIS-762, DATAREDIS-869 void factoryUsesElastiCacheMasterWithoutMaster() { - assumeTrue(String.format("No replicas connected to %s:%s.", SettingsUtils.getHost(), SettingsUtils.getPort()), + assumeTrue("No replicas connected to %s:%d.".formatted(SettingsUtils.getHost(), SettingsUtils.getPort()), connection.info("replication").getProperty("connected_slaves", "0").compareTo("0") > 0); LettuceClientConfiguration configuration = LettuceTestClientConfiguration.builder().readFrom(ReadFrom.MASTER) @@ -481,7 +481,7 @@ void factoryUsesElastiCacheMasterWithoutMaster() { @Test // DATAREDIS-580, DATAREDIS-869 void factoryUsesMasterReplicaConnections() { - assumeTrue(String.format("No replicas connected to %s:%s.", SettingsUtils.getHost(), SettingsUtils.getPort()), + assumeTrue("No replicas connected to %s:%d".formatted(SettingsUtils.getHost(), SettingsUtils.getPort()), connection.info("replication").getProperty("connected_slaves", "0").compareTo("0") > 0); LettuceClientConfiguration configuration = LettuceTestClientConfiguration.builder().readFrom(ReadFrom.SLAVE) @@ -489,6 +489,7 @@ void factoryUsesMasterReplicaConnections() { LettuceConnectionFactory factory = new LettuceConnectionFactory(SettingsUtils.standaloneConfiguration(), configuration); + factory.start(); RedisConnection connection = factory.getConnection(); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommandsIntegrationTests.java index 75a0b75a3a..0a16832f67 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommandsIntegrationTests.java @@ -111,7 +111,7 @@ void evalShaShouldFail() { @ParameterizedRedisTest // DATAREDIS-683 void evalShouldReturnStatus() { - ByteBuffer script = wrap(String.format("return redis.call('set','%s','ghk')", SAME_SLOT_KEY_1)); + ByteBuffer script = wrap("return redis.call('set','%s','ghk')".formatted(SAME_SLOT_KEY_1)); connection.scriptingCommands().eval(script, ReturnType.STATUS, 1, SAME_SLOT_KEY_1_BBUFFER.duplicate()) .as(StepVerifier::create) // diff --git a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java index 65e57819f3..94ccf69b29 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java @@ -821,7 +821,7 @@ private static void waitUntilKeyIsGone(RedisTemplate template, String while (template.hasKey(key)) { if (waitedMs > limitMs) { - throw new TimeoutException(String.format("Key '%s' after %d %s still present", key, timeout, timeUnit)); + throw new TimeoutException("Key '%s' after %d %s still present".formatted(key, timeout, timeUnit)); } Thread.sleep(sleepMs); diff --git a/src/test/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntityUnitTests.java b/src/test/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntityUnitTests.java index f26bbf93da..f2d653210a 100644 --- a/src/test/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntityUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntityUnitTests.java @@ -34,7 +34,6 @@ * @author Christoph Strobl * @author Mark Paluch * @param - * @param */ @ExtendWith(MockitoExtension.class) class BasicRedisPersistentEntityUnitTests { @@ -66,7 +65,8 @@ void addingMultipleIdPropertiesWithoutAnExplicitOneThrowsException() { entity.addPersistentProperty(property1); assertThatExceptionOfType(MappingException.class).isThrownBy(() -> entity.addPersistentProperty(property2)) - .withMessageContaining("Attempt to add id property").withMessageContaining("but already have an property"); + .withMessageContaining("Attempt to add id property") + .withMessageContaining("but already have a property"); } @Test // DATAREDIS-425 @@ -84,7 +84,7 @@ void addingMultipleExplicitIdPropertiesThrowsException() { entity.addPersistentProperty(property1); assertThatExceptionOfType(MappingException.class).isThrownBy(() -> entity.addPersistentProperty(property2)) .withMessageContaining("Attempt to add explicit id property") - .withMessageContaining("but already have an property"); + .withMessageContaining("but already have a property"); } @Test // DATAREDIS-425 diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailableCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailableCondition.java index f1c73953df..88ac8dcbc5 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailableCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailableCondition.java @@ -57,10 +57,10 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con socket.connect(new InetSocketAddress(SettingsUtils.getHost(), annotation.value()), 100); - return enabled(String.format("Connection successful to Redis at %s:%d", SettingsUtils.getHost(), + return enabled("Connection successful to Redis at %s:%d".formatted(SettingsUtils.getHost(), annotation.value())); } catch (IOException ex) { - return disabled(String.format("Cannot connect to Redis at %s:%d (%s)", SettingsUtils.getHost(), + return disabled("Cannot connect to Redis at %s:%d (%s)".formatted(SettingsUtils.getHost(), annotation.value(), ex)); } } diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterCondition.java index f67cf4d3f8..dba3c6071b 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterCondition.java @@ -48,11 +48,11 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con } if (RedisDetector.isClusterAvailable()) { - return enabled(String.format("Connection successful to Redis Cluster at %s:%d", SettingsUtils.getHost(), + return enabled("Connection successful to Redis Cluster at %s:%d".formatted(SettingsUtils.getHost(), SettingsUtils.getClusterPort())); } - return disabled(String.format("Cannot connect to Redis Cluster at %s:%d", SettingsUtils.getHost(), + return disabled("Cannot connect to Redis Cluster at %s:%d".formatted(SettingsUtils.getHost(), SettingsUtils.getClusterPort())); } diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriverCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriverCondition.java index d4fbf0e78b..b65e66d040 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriverCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriverCondition.java @@ -81,13 +81,12 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con } if (!foundMatch) { - return disabled(String.format("Driver %s not supported; Supported driver(s): %s", - formatUnsupportedDriver(value), - Arrays.toString(annotation.value()))); + return disabled("Driver %s not supported; Supported driver(s): %s" + .formatted(formatUnsupportedDriver(value), Arrays.toString(annotation.value()))); } } - return enabled("Found enabled driver(s): " + Arrays.toString(annotation.value())); + return enabled("Found enabled driver(s): %s".formatted(Arrays.toString(annotation.value()))); } diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelCondition.java index 9a59d80ba8..2d7fe35671 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelCondition.java @@ -52,12 +52,11 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con if (RedisDetector.canConnectToPort(annotation.value())) { - return enabled(String.format("Connection successful to Redis Sentinel at %s:%d", SettingsUtils.getHost(), + return enabled("Connection successful to Redis Sentinel at %s:%d".formatted(SettingsUtils.getHost(), annotation.value())); } - return disabled( - String.format("Cannot connect to Redis Sentinel at %s:%d", SettingsUtils.getHost(), annotation.value())); - + return disabled("Cannot connect to Redis Sentinel at %s:%d".formatted(SettingsUtils.getHost(), + annotation.value())); } } diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersionCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersionCondition.java index f23bfd11aa..9000158666 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersionCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersionCondition.java @@ -61,11 +61,14 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con } }, RedisConditions.class); - boolean versionMet = conditions.hasVersionGreaterOrEqualsTo(requiredVersion); - return versionMet - ? enabled( - String.format("Enabled on version %s (actual version: %s)", requiredVersion, conditions.getRedisVersion())) - : disabled(String.format("Disabled, version %s not available on Redis version %s", requiredVersion, - conditions.getRedisVersion())); + boolean requiredVersionMet = conditions.hasVersionGreaterOrEqualsTo(requiredVersion); + + if (requiredVersionMet) { + return enabled("Enabled on version %s; actual version: %s".formatted(requiredVersion, + conditions.getRedisVersion())); + } + + return disabled("Disabled; version %s not available on Redis version %s".formatted(requiredVersion, + conditions.getRedisVersion())); } } diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTestExtension.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTestExtension.java index 7b89c271bb..3cb80348f2 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTestExtension.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTestExtension.java @@ -61,10 +61,11 @@ public boolean supportsTestTemplate(ExtensionContext context) { ParameterizedTestContext methodContext = new ParameterizedTestContext(testMethod); ParameterizedTestContext constructorContext = new ParameterizedTestContext(declaredConstructor); - Preconditions.condition(methodContext.hasPotentiallyValidSignature(), - () -> String.format("@ParameterizedRedisTest method [%s] declares formal parameters in an invalid order: " + Preconditions.condition(methodContext.hasPotentiallyValidSignature(), () -> + ("@ParameterizedRedisTest method [%s] declares formal parameters in an invalid order: " + "argument aggregators must be declared after any indexed arguments " - + "and before any arguments resolved by another ParameterResolver.", testMethod.toGenericString())); + + "and before any arguments resolved by another ParameterResolver.") + .formatted(testMethod.toGenericString())); getStore(context).put(METHOD_CONTEXT_KEY, methodContext); getStore(context).put(CONSTRUCTOR_CONTEXT_KEY, constructorContext); @@ -115,10 +116,9 @@ private ArgumentsProvider instantiateArgumentsProvider(Class String.format( - "Configuration error: @ParameterizedRedisTest on method [%s] must be declared with a non-empty name.", - templateMethod)); + + String pattern = Preconditions.notBlank(parameterizedTest.name().trim(), () -> + "Configuration error: @ParameterizedRedisTest on method [%s] must be declared with a non-empty name" + .formatted(templateMethod)); + return new ParameterizedTestNameFormatter(pattern, displayName, methodContext, argumentMaxLength); } diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestNameFormatter.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestNameFormatter.java index d67c7399ac..b88598baa4 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestNameFormatter.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestNameFormatter.java @@ -53,9 +53,8 @@ String format(int invocationIndex, Object... arguments) { try { return formatSafely(invocationIndex, arguments); } catch (Exception ex) { - String message = "The display name pattern defined for the parameterized test is invalid; " - + "See nested exception for further details."; - throw new JUnitException(message, ex); + throw new JUnitException("The display name pattern defined for the parameterized test is invalid;" + + " See nested exception for further details.", ex); } } diff --git a/src/test/java/org/springframework/data/redis/test/util/MockitoUtils.java b/src/test/java/org/springframework/data/redis/test/util/MockitoUtils.java index 5a384bb838..d9928f6dfa 100644 --- a/src/test/java/org/springframework/data/redis/test/util/MockitoUtils.java +++ b/src/test/java/org/springframework/data/redis/test/util/MockitoUtils.java @@ -68,7 +68,7 @@ public boolean matches(Invocation actual) { @Override public String toString() { - return String.format("%s for method: %s", mode, method); + return "%s for method: %s".formatted(mode, method); } })); } diff --git a/src/test/java/org/springframework/data/redis/test/util/RedisTestData.java b/src/test/java/org/springframework/data/redis/test/util/RedisTestData.java index c0ca6e6f8d..e1710741f5 100644 --- a/src/test/java/org/springframework/data/redis/test/util/RedisTestData.java +++ b/src/test/java/org/springframework/data/redis/test/util/RedisTestData.java @@ -86,7 +86,7 @@ public RedisBucketAssert containingTypeHint(String path, Class type) { isNotNull(); - String hint = String.format("Type hint for <%s> at <%s>", type.getName(), path); + String hint = "Type hint for <%s> at <%s>".formatted(type.getName(), path); if (!actual.containsKey(path)) { From 9c5f21b8405e051526164c4957360a11ccb1fd2e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 10 Sep 2024 14:01:02 +0200 Subject: [PATCH 056/187] Polishing. Simplify code to use well-known Spring patterns. Original pull request: #2752 See #2751 --- .../data/redis/cache/RedisCache.java | 4 +- .../data/redis/cache/RedisCacheManager.java | 30 ++-- .../connection/RedisClusterConfiguration.java | 11 +- .../redis/connection/RedisClusterNode.java | 13 +- .../lettuce/LettuceConnectionFactory.java | 8 +- .../core/DefaultReactiveZSetOperations.java | 11 +- .../JdkSerializationRedisSerializer.java | 9 +- .../data/redis/util/RedisAssertions.java | 2 + .../redis/util/RedisAssertionsUnitTests.java | 137 ------------------ 9 files changed, 58 insertions(+), 167 deletions(-) delete mode 100644 src/test/java/org/springframework/data/redis/util/RedisAssertionsUnitTests.java diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCache.java b/src/main/java/org/springframework/data/redis/cache/RedisCache.java index c900387fee..41cd0330b2 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCache.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCache.java @@ -37,7 +37,6 @@ import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.util.ByteUtils; -import org.springframework.data.redis.util.RedisAssertions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -82,8 +81,7 @@ public class RedisCache extends AbstractValueAdaptingCache { */ protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfiguration) { - super(RedisAssertions.requireNonNull(cacheConfiguration, "CacheConfiguration must not be null") - .getAllowCacheNullValues()); + super(cacheConfiguration.getAllowCacheNullValues()); Assert.notNull(name, "Name must not be null"); Assert.notNull(cacheWriter, "CacheWriter must not be null"); diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java index 1df8208ef4..23d51456c7 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java @@ -27,7 +27,6 @@ import org.springframework.cache.CacheManager; import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.util.RedisAssertions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -103,10 +102,11 @@ public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration d private RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowRuntimeCacheCreation) { - this.defaultCacheConfiguration = RedisAssertions.requireNonNull(defaultCacheConfiguration, - "DefaultCacheConfiguration must not be null"); + Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null"); + Assert.notNull(cacheWriter, "CacheWriter must not be null"); - this.cacheWriter = RedisAssertions.requireNonNull(cacheWriter, "CacheWriter must not be null"); + this.defaultCacheConfiguration = defaultCacheConfiguration; + this.cacheWriter = cacheWriter; this.initialCacheConfiguration = new LinkedHashMap<>(); this.allowRuntimeCacheCreation = allowRuntimeCacheCreation; } @@ -423,7 +423,10 @@ public static class RedisCacheManagerBuilder { * @see org.springframework.data.redis.cache.RedisCacheWriter */ public static RedisCacheManagerBuilder fromCacheWriter(RedisCacheWriter cacheWriter) { - return new RedisCacheManagerBuilder(RedisAssertions.requireNonNull(cacheWriter, "CacheWriter must not be null")); + + Assert.notNull(cacheWriter, "CacheWriter must not be null"); + + return new RedisCacheManagerBuilder(cacheWriter); } /** @@ -534,7 +537,10 @@ public RedisCacheManagerBuilder cacheDefaults(RedisCacheConfiguration defaultCac * @since 2.3 */ public RedisCacheManagerBuilder cacheWriter(RedisCacheWriter cacheWriter) { - this.cacheWriter = RedisAssertions.requireNonNull(cacheWriter, "CacheWriter must not be null"); + + Assert.notNull(cacheWriter, "CacheWriter must not be null"); + + this.cacheWriter = cacheWriter; return this; } @@ -558,8 +564,10 @@ public RedisCacheManagerBuilder enableStatistics() { */ public RedisCacheManagerBuilder initialCacheNames(Set cacheNames) { - RedisAssertions.requireNonNull(cacheNames, "CacheNames must not be null") - .forEach(it -> withCacheConfiguration(it, defaultCacheConfiguration)); + Assert.notNull(cacheNames, "CacheNames must not be null"); + Assert.noNullElements(cacheNames, "CacheNames must not be null"); + + cacheNames.forEach(it -> withCacheConfiguration(it, defaultCacheConfiguration)); return this; } @@ -603,9 +611,9 @@ public RedisCacheManagerBuilder withCacheConfiguration(String cacheName, public RedisCacheManagerBuilder withInitialCacheConfigurations( Map cacheConfigurations) { - RedisAssertions.requireNonNull(cacheConfigurations, "CacheConfigurations must not be null") - .forEach((cacheName, cacheConfiguration) -> RedisAssertions.requireNonNull(cacheConfiguration, - "RedisCacheConfiguration for cache [%s] must not be null", cacheName)); + Assert.notNull(cacheConfigurations, "CacheConfigurations must not be null!"); + cacheConfigurations.forEach((cacheName, configuration) -> Assert.notNull(configuration, + String.format("RedisCacheConfiguration for cache %s must not be null!", cacheName))); this.initialCaches.putAll(cacheConfigurations); diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java index 620e132d02..057527ed0d 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java @@ -25,7 +25,6 @@ import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; import org.springframework.data.redis.connection.RedisConfiguration.ClusterConfiguration; -import org.springframework.data.redis.util.RedisAssertions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.NumberUtils; @@ -161,7 +160,10 @@ public Set getClusterNodes() { * @param node must not be {@literal null}. */ public void addClusterNode(RedisNode node) { - this.clusterNodes.add(RedisAssertions.requireNonNull(node, "ClusterNode must not be null")); + + Assert.notNull(node, "ClusterNode must not be null"); + + this.clusterNodes.add(node); } /** @@ -211,7 +213,10 @@ public String getUsername() { @Override public void setPassword(RedisPassword password) { - this.password = RedisAssertions.requireNonNull(password, "RedisPassword must not be null"); + + Assert.notNull(password, "RedisPassword must not be null"); + + this.password = password; } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterNode.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterNode.java index 7f84a2110d..99cd937075 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterNode.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterNode.java @@ -22,7 +22,6 @@ import java.util.LinkedHashSet; import java.util.Set; -import org.springframework.data.redis.util.RedisAssertions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -76,7 +75,9 @@ public RedisClusterNode(String id) { this(SlotRange.empty()); - this.id = RedisAssertions.requireNonNull(id, "Id must not be null"); + Assert.notNull(id, "Id must not be null"); + + this.id = id; } /** @@ -86,8 +87,10 @@ public RedisClusterNode(String id) { */ public RedisClusterNode(SlotRange slotRange) { + Assert.notNull(slotRange, "SlotRange must not be null"); + this.flags = Collections.emptySet(); - this.slotRange = RedisAssertions.requireNonNull(slotRange,"SlotRange must not be null"); + this.slotRange = slotRange; } /** @@ -101,8 +104,10 @@ public RedisClusterNode(String host, int port, SlotRange slotRange) { super(host, port); + Assert.notNull(slotRange, "SlotRange must not be null"); + this.flags = Collections.emptySet(); - this.slotRange = RedisAssertions.requireNonNull(slotRange,"SlotRange must not be null"); + this.slotRange = slotRange; } /** diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java index fdbc882a99..6d54316548 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java @@ -64,7 +64,6 @@ import org.springframework.data.redis.connection.RedisConfiguration.ClusterConfiguration; import org.springframework.data.redis.connection.RedisConfiguration.WithDatabaseIndex; import org.springframework.data.redis.connection.RedisConfiguration.WithPassword; -import org.springframework.data.redis.util.RedisAssertions; import org.springframework.data.util.Optionals; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -670,8 +669,11 @@ public AbstractRedisClient getNativeClient() { */ public AbstractRedisClient getRequiredNativeClient() { - return RedisAssertions.requireState(getNativeClient(), - "Client not yet initialized; Did you forget to call initialize the bean"); + AbstractRedisClient client = getNativeClient(); + + Assert.state(client != null, "Client not yet initialized; Did you forget to call initialize the bean"); + + return client; } @Nullable diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java index 40869055e1..5fc762f517 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java @@ -27,6 +27,7 @@ import java.util.function.Function; import org.reactivestreams.Publisher; + import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Range; import org.springframework.data.redis.connection.Limit; @@ -38,7 +39,6 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.util.ByteUtils; -import org.springframework.data.redis.util.RedisAssertions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -745,8 +745,13 @@ private V readValue(ByteBuffer buffer) { private V readRequiredValue(ByteBuffer buffer) { - return RedisAssertions.requireNonNull(readValue(buffer), - () -> new InvalidDataAccessApiUsageException("Deserialized sorted set value is null")); + V value = readValue(buffer); + + if (value == null) { + throw new InvalidDataAccessApiUsageException("Deserialized sorted set value is null"); + } + + return value; } private TypedTuple readTypedTuple(Tuple raw) { diff --git a/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java index 00c79c6f8a..d25ea9c395 100644 --- a/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java @@ -20,8 +20,8 @@ import org.springframework.core.serializer.DefaultSerializer; import org.springframework.core.serializer.support.DeserializingConverter; import org.springframework.core.serializer.support.SerializingConverter; -import org.springframework.data.redis.util.RedisAssertions; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * Java Serialization {@link RedisSerializer}. @@ -77,8 +77,11 @@ public JdkSerializationRedisSerializer(@Nullable ClassLoader classLoader) { public JdkSerializationRedisSerializer(Converter serializer, Converter deserializer) { - this.serializer = RedisAssertions.requireNonNull(serializer, "Serializer must not be null"); - this.deserializer = RedisAssertions.requireNonNull(deserializer, "Deserializer must not be null"); + Assert.notNull(serializer, "Serializer must not be null"); + Assert.notNull(deserializer, "Deserializer must not be null"); + + this.serializer = serializer; + this.deserializer = deserializer; } @Override diff --git a/src/main/java/org/springframework/data/redis/util/RedisAssertions.java b/src/main/java/org/springframework/data/redis/util/RedisAssertions.java index 2b0272de24..a218f762ae 100644 --- a/src/main/java/org/springframework/data/redis/util/RedisAssertions.java +++ b/src/main/java/org/springframework/data/redis/util/RedisAssertions.java @@ -25,7 +25,9 @@ * * @author John Blum * @since 3.1.0 + * @deprecated since 3.3, will be removed in a future revision in favor of Spring's {@link Assert} utility. */ +@Deprecated(since = "3.3", forRemoval = true) public abstract class RedisAssertions { /** diff --git a/src/test/java/org/springframework/data/redis/util/RedisAssertionsUnitTests.java b/src/test/java/org/springframework/data/redis/util/RedisAssertionsUnitTests.java deleted file mode 100644 index d9fbe94d1c..0000000000 --- a/src/test/java/org/springframework/data/redis/util/RedisAssertionsUnitTests.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2017-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.util; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.dao.InvalidDataAccessApiUsageException; - -/** - * Unit Tests for {@link RedisAssertions}. - * - * @author John Blum - */ -@ExtendWith(MockitoExtension.class) -class RedisAssertionsUnitTests { - - @Mock - private Supplier mockSupplier; - - @Test - void requireNonNullWithMessageAndArgumentsIsSuccessful() { - assertThat(RedisAssertions.requireNonNull("test", "Test message")).isEqualTo("test"); - } - - @Test - void requireNonNullWithMessageAndArgumentsThrowsIllegalArgumentException() { - - assertThatIllegalArgumentException() - .isThrownBy(() -> RedisAssertions.requireNonNull(null, "This is a %s", "test")) - .withMessage("This is a test") - .withNoCause(); - } - - @Test - void requireNonNullWithSupplierIsSuccessful() { - - assertThat(RedisAssertions.requireNonNull("mock", this.mockSupplier)).isEqualTo("mock"); - - verifyNoInteractions(this.mockSupplier); - } - - @Test - void requireNonNullWithSupplierThrowsIllegalArgumentException() { - - doReturn("Mock message").when(this.mockSupplier).get(); - - assertThatIllegalArgumentException() - .isThrownBy(() -> RedisAssertions.requireNonNull(null, this.mockSupplier)) - .withMessage("Mock message") - .withNoCause(); - - verify(this.mockSupplier, times(1)).get(); - verifyNoMoreInteractions(this.mockSupplier); - } - - @Test - void requireNonNullWithRuntimeExceptionSupplierIsSuccessful() { - - assertThat(RedisAssertions.requireNonNull("mock", () -> new InvalidDataAccessApiUsageException("TEST"))) - .isEqualTo("mock"); - } - - @Test - @SuppressWarnings("all") - void requireNonNullWithThrowsRuntimeException() { - - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) - .isThrownBy(() -> RedisAssertions.requireNonNull(null, - () -> new InvalidDataAccessApiUsageException("TEST"))) - .withMessage("TEST") - .withNoCause(); - } - - @Test - void requireStateWithMessageAndArgumentsIsSuccessful() { - assertThat(RedisAssertions.requireState("test", "Mock message")).isEqualTo("test"); - } - - @Test - void requireStateWithMessageAndArgumentsThrowsIllegalStateException() { - - assertThatIllegalStateException() - .isThrownBy(() -> RedisAssertions.requireState(null, "This is a %s", "test")) - .withMessage("This is a test") - .withNoCause(); - } - - @Test - void requireStateWithSupplierIsSuccessful() { - - assertThat(RedisAssertions.requireState("test", this.mockSupplier)).isEqualTo("test"); - - verifyNoInteractions(this.mockSupplier); - } - - @Test - void requiredStateWithSupplierThrowsIllegalStateException() { - - doReturn("Mock message").when(this.mockSupplier).get(); - - assertThatIllegalStateException() - .isThrownBy(() -> RedisAssertions.requireState(null, this.mockSupplier)) - .withMessage("Mock message") - .withNoCause(); - - verify(this.mockSupplier, times(1)).get(); - verifyNoMoreInteractions(this.mockSupplier); - } -} From b22f1ab84ca47ce2434b51ca769215d6f584d358 Mon Sep 17 00:00:00 2001 From: Ivan Kripakov Date: Tue, 10 Sep 2024 23:52:16 +0400 Subject: [PATCH 057/187] Enable `eval` and `evalsha` in Jedis pipeline/transaction. Original pull request: #2988 Closes #1455 --- .../jedis/JedisScriptingCommands.java | 14 ++-- ...disConnectionPipelineIntegrationTests.java | 78 +----------------- ...ConnectionTransactionIntegrationTests.java | 82 ++++++------------- 3 files changed, 33 insertions(+), 141 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java index 13eb3178b4..92d758b000 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java @@ -26,6 +26,7 @@ /** * @author Mark Paluch + * @author Ivan Kripakov * @since 2.0 */ class JedisScriptingCommands implements RedisScriptingCommands { @@ -76,11 +77,11 @@ public List scriptExists(String... scriptSha1) { public T eval(byte[] script, ReturnType returnType, int numKeys, byte[]... keysAndArgs) { Assert.notNull(script, "Script must not be null"); - assertDirectMode(); JedisScriptReturnConverter converter = new JedisScriptReturnConverter(returnType); - return (T) connection.invoke().from(it -> it.eval(script, numKeys, keysAndArgs)).getOrElse(converter, - () -> converter.convert(null)); + return (T) connection.invoke() + .from(it -> it.eval(script, numKeys, keysAndArgs), t -> t.eval(script, numKeys, keysAndArgs)) + .getOrElse(converter, () -> converter.convert(null)); } @Override @@ -93,11 +94,12 @@ public T evalSha(String scriptSha1, ReturnType returnType, int numKeys, byte public T evalSha(byte[] scriptSha, ReturnType returnType, int numKeys, byte[]... keysAndArgs) { Assert.notNull(scriptSha, "Script digest must not be null"); - assertDirectMode(); JedisScriptReturnConverter converter = new JedisScriptReturnConverter(returnType); - return (T) connection.invoke().from(it -> it.evalsha(scriptSha, numKeys, keysAndArgs)).getOrElse(converter, - () -> converter.convert(null)); + return (T) connection.invoke() + .from(it -> it.evalsha(scriptSha, numKeys, keysAndArgs), t -> t.evalsha(scriptSha, numKeys, keysAndArgs)) + .getOrElse(converter, () -> converter.convert(null) + ); } private void assertDirectMode() { diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java index a5cacfb2df..dae84554c9 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java @@ -37,6 +37,7 @@ * @author Christoph Strobl * @author Thomas Darimont * @author Mark Paluch + * @author Ivan Kripakov */ @ExtendWith(SpringExtension.class) @ContextConfiguration("JedisConnectionIntegrationTests-context.xml") @@ -75,83 +76,6 @@ public void testClosePoolPipelinedDbSelect() { } // Unsupported Ops - @Test - public void testScriptLoadEvalSha() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testScriptLoadEvalSha); - } - - @Test - public void testEvalShaArrayStrings() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalShaArrayStrings); - } - - @Test - public void testEvalShaArrayBytes() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalShaArrayBytes); - } - - @Test - @Disabled - public void testEvalShaNotFound() {} - - @Test - @Disabled - public void testEvalShaArrayError() {} - - @Test - public void testEvalReturnString() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnString); - } - - @Test - public void testEvalReturnNumber() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnNumber); - } - - @Test - public void testEvalReturnSingleOK() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnSingleOK); - } - - @Test - @Disabled - public void testEvalReturnSingleError() {} - - @Test - public void testEvalReturnFalse() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnFalse); - } - - @Test - public void testEvalReturnTrue() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnTrue); - } - - @Test - public void testEvalReturnArrayStrings() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnArrayStrings); - } - - @Test - public void testEvalReturnArrayNumbers() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnArrayNumbers); - } - - @Test - public void testEvalReturnArrayOKs() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnArrayOKs); - } - - @Test - public void testEvalReturnArrayFalses() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnArrayFalses); - } - - @Test - public void testEvalReturnArrayTrues() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testEvalReturnArrayTrues); - } - @Test public void testScriptExists() { assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testScriptExists); diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java index 45706608a2..5d61de88e3 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java @@ -36,6 +36,7 @@ * * @author Jennifer Hickey * @author Mark Paluch + * @author Ivan Kripakov */ @ExtendWith(SpringExtension.class) @ContextConfiguration("JedisConnectionIntegrationTests-context.xml") @@ -56,79 +57,44 @@ public void tearDown() { @Disabled("Jedis issue: Transaction tries to return String instead of List") public void testGetConfig() {} - // Unsupported Ops - @Test - @Disabled - public void testScriptLoadEvalSha() {} - - @Test - @Disabled - public void testEvalShaArrayStrings() {} - - @Test - @Disabled - public void testEvalShaArrayBytes() {} - @Test - @Disabled - public void testEvalShaNotFound() {} + public void testEvalShaNotFound() { + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> { + connection.evalSha("somefakesha", ReturnType.VALUE, 2, "key1", "key2"); + getResults(); + }); + } @Test public void testEvalShaArrayError() { assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) - .isThrownBy(() -> connection.evalSha("notasha", ReturnType.MULTI, 1, "key1", "arg1")); + .isThrownBy(() -> { + connection.evalSha("notasha", ReturnType.MULTI, 1, "key1", "arg1"); + getResults(); + }); } @Test public void testEvalArrayScriptError() { assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) - .isThrownBy(() -> connection.eval("return {1,2", ReturnType.MULTI, 1, "foo", "bar")); + .isThrownBy(() -> { + connection.eval("return {1,2", ReturnType.MULTI, 1, "foo", "bar"); + getResults(); + }); } @Test - @Disabled - public void testEvalReturnString() {} - - @Test - @Disabled - public void testEvalReturnNumber() {} - - @Test - @Disabled - public void testEvalReturnSingleOK() {} - - @Test - @Disabled - public void testEvalReturnSingleError() {} - - @Test - @Disabled - public void testEvalReturnFalse() {} - - @Test - @Disabled - public void testEvalReturnTrue() {} - - @Test - @Disabled - public void testEvalReturnArrayStrings() {} - - @Test - @Disabled - public void testEvalReturnArrayNumbers() {} - - @Test - @Disabled - public void testEvalReturnArrayOKs() {} - - @Test - @Disabled - public void testEvalReturnArrayFalses() {} + public void testEvalReturnSingleError() { + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(()-> { + connection.eval("return redis.call('expire','foo')", ReturnType.BOOLEAN, 0); + getResults(); + }); + } - @Test - @Disabled - public void testEvalReturnArrayTrues() {} + // Unsupported Ops @Test @Disabled public void testScriptExists() {} From 297ee410dc1fcdf50ac7af5a62be8fc4bce62ef5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 11 Sep 2024 11:31:24 +0200 Subject: [PATCH 058/187] Polishing. Enable SCRIPT FLUSH and SCRIPT KILL for pipelining/transaction usage. Use lambdas where possible. Original pull request: #2988 See #1455 --- .../jedis/JedisScriptingCommands.java | 35 ++++++++----------- ...ConnectionTransactionIntegrationTests.java | 7 ---- ...disConnectionPipelineIntegrationTests.java | 14 -------- ...ConnectionTransactionIntegrationTests.java | 12 ------- 4 files changed, 14 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java index 92d758b000..0c114dacb5 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java @@ -16,10 +16,10 @@ package org.springframework.data.redis.connection.jedis; import redis.clients.jedis.Jedis; +import redis.clients.jedis.commands.ScriptingKeyPipelineBinaryCommands; import java.util.List; -import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.RedisScriptingCommands; import org.springframework.data.redis.connection.ReturnType; import org.springframework.util.Assert; @@ -31,6 +31,7 @@ */ class JedisScriptingCommands implements RedisScriptingCommands { + private static final byte[] SAMPLE_KEY = new byte[0]; private final JedisConnection connection; JedisScriptingCommands(JedisConnection connection) { @@ -39,27 +40,21 @@ class JedisScriptingCommands implements RedisScriptingCommands { @Override public void scriptFlush() { - - assertDirectMode(); - - connection.invoke().just(Jedis::scriptFlush); + connection.invoke().just(Jedis::scriptFlush, it -> it.scriptFlush(SAMPLE_KEY)); } @Override public void scriptKill() { - - assertDirectMode(); - - connection.invoke().just(Jedis::scriptKill); + connection.invoke().just(Jedis::scriptKill, it -> it.scriptKill(SAMPLE_KEY)); } @Override public String scriptLoad(byte[] script) { Assert.notNull(script, "Script must not be null"); - assertDirectMode(); - return connection.invoke().from(it -> it.scriptLoad(script)).get(JedisConverters::toString); + return connection.invoke().from(it -> it.scriptLoad(script), it -> it.scriptLoad(script, SAMPLE_KEY)) + .get(JedisConverters::toString); } @Override @@ -67,9 +62,13 @@ public List scriptExists(String... scriptSha1) { Assert.notNull(scriptSha1, "Script digests must not be null"); Assert.noNullElements(scriptSha1, "Script digests must not contain null elements"); - assertDirectMode(); - return connection.invoke().just(it -> it.scriptExists(scriptSha1)); + byte[][] sha1 = new byte[scriptSha1.length][]; + for (int i = 0; i < scriptSha1.length; i++) { + sha1[i] = JedisConverters.toBytes(scriptSha1[i]); + } + + return connection.invoke().just(it -> it.scriptExists(scriptSha1), it -> it.scriptExists(SAMPLE_KEY, sha1)); } @Override @@ -80,7 +79,7 @@ public T eval(byte[] script, ReturnType returnType, int numKeys, byte[]... k JedisScriptReturnConverter converter = new JedisScriptReturnConverter(returnType); return (T) connection.invoke() - .from(it -> it.eval(script, numKeys, keysAndArgs), t -> t.eval(script, numKeys, keysAndArgs)) + .from(Jedis::eval, ScriptingKeyPipelineBinaryCommands::eval, script, numKeys, keysAndArgs) .getOrElse(converter, () -> converter.convert(null)); } @@ -97,15 +96,9 @@ public T evalSha(byte[] scriptSha, ReturnType returnType, int numKeys, byte[ JedisScriptReturnConverter converter = new JedisScriptReturnConverter(returnType); return (T) connection.invoke() - .from(it -> it.evalsha(scriptSha, numKeys, keysAndArgs), t -> t.evalsha(scriptSha, numKeys, keysAndArgs)) + .from(Jedis::evalsha, ScriptingKeyPipelineBinaryCommands::evalsha, scriptSha, numKeys, keysAndArgs) .getOrElse(converter, () -> converter.convert(null) ); } - private void assertDirectMode() { - if (connection.isQueueing() || connection.isPipelined()) { - throw new InvalidDataAccessApiUsageException("Scripting commands not supported in pipelining/transaction mode"); - } - } - } diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionTransactionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionTransactionIntegrationTests.java index 1b5c4672f5..1ece6b7ed7 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionTransactionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionTransactionIntegrationTests.java @@ -134,13 +134,6 @@ public void testWatchWhileInTx() { .isThrownBy(() -> connection.watch("foo".getBytes())); } - @Test - public void testScriptKill() { - // Impossible to call script kill in a tx because you can't issue the - // exec command while Redis is running a script - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(() -> connection.scriptKill()); - } - @Test // DATAREDIS-417 @Disabled @Override diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java index dae84554c9..cbd0edae68 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java @@ -76,20 +76,6 @@ public void testClosePoolPipelinedDbSelect() { } // Unsupported Ops - @Test - public void testScriptExists() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::testScriptExists); - } - - @Test - public void testScriptKill() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(() -> connection.scriptKill()); - } - - @Test - @Disabled - public void testScriptFlush() {} - @Test // DATAREDIS-269 public void clientSetNameWorksCorrectly() { assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(super::clientSetNameWorksCorrectly); diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java index 5d61de88e3..ba43a4fd2b 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java @@ -95,18 +95,6 @@ public void testEvalReturnSingleError() { // Unsupported Ops - @Test - @Disabled - public void testScriptExists() {} - - @Test - @Disabled - public void testScriptKill() {} - - @Test - @Disabled - public void testScriptFlush() {} - @Test @Disabled public void testInfoBySection() {} From 1514461561c7d3df9e7a52a3469f43f846e3af32 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 11 Sep 2024 08:52:59 +0200 Subject: [PATCH 059/187] Use value object for topology caching. We now use a value object for caching the topology to avoid races in updating the cache timestamp. Also, we set the cache timestamp after obtaining the topology to avoid that I/O latency expires the topology cache. Closes: #2986 Original Pull Request: #2989 --- .../jedis/JedisClusterConnection.java | 50 ++++++++++++++----- .../jedis/JedisClusterConnectionTests.java | 19 +++++++ 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java index ace9d4acb6..625c085729 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java @@ -805,13 +805,11 @@ public void returnResourceForSpecificNode(RedisClusterNode node, Object client) */ public static class JedisClusterTopologyProvider implements ClusterTopologyProvider { - private long time = 0; + private final JedisCluster cluster; private final long cacheTimeMs; - private @Nullable ClusterTopology cached; - - private final JedisCluster cluster; + private volatile @Nullable JedisClusterTopology cached; /** * Create new {@link JedisClusterTopologyProvider}. Uses a default cache timeout of 100 milliseconds. @@ -842,12 +840,12 @@ public JedisClusterTopologyProvider(JedisCluster cluster, Duration cacheTimeout) @Override public ClusterTopology getTopology() { - if (cached != null && shouldUseCachedValue()) { - return cached; + JedisClusterTopology topology = cached; + if (shouldUseCachedValue(topology)) { + return topology; } Map errors = new LinkedHashMap<>(); - List> list = new ArrayList<>(cluster.getClusterNodes().entrySet()); Collections.shuffle(list); @@ -856,13 +854,10 @@ public ClusterTopology getTopology() { try (Connection connection = entry.getValue().getResource()) { - time = System.currentTimeMillis(); Set nodes = Converters.toSetOfRedisClusterNodes(new Jedis(connection).clusterNodes()); - - cached = new ClusterTopology(nodes); - - return cached; + topology = cached = new JedisClusterTopology(nodes, System.currentTimeMillis()); + return topology; } catch (Exception ex) { errors.put(entry.getKey(), ex); @@ -887,9 +882,38 @@ public ClusterTopology getTopology() { * topology. * @see #JedisClusterTopologyProvider(JedisCluster, Duration) * @since 2.2 + * @deprecated since 3.3.4, use {@link #shouldUseCachedValue(JedisClusterTopology)} instead. */ + @Deprecated(since = "3.3.4") protected boolean shouldUseCachedValue() { - return time + cacheTimeMs > System.currentTimeMillis(); + return false; + } + + /** + * Returns whether {@link #getTopology()} should return the cached {@link JedisClusterTopology}. Uses a time-based + * caching. + * + * @return {@literal true} to use the cached {@link ClusterTopology}; {@literal false} to fetch a new cluster + * topology. + * @see #JedisClusterTopologyProvider(JedisCluster, Duration) + * @since 3.3.4 + */ + protected boolean shouldUseCachedValue(@Nullable JedisClusterTopology topology) { + return topology != null && topology.getTime() + cacheTimeMs > System.currentTimeMillis(); + } + } + + protected static class JedisClusterTopology extends ClusterTopology { + + private final long time; + + public JedisClusterTopology(Set nodes, long time) { + super(nodes); + this.time = time; + } + + public long getTime() { + return time; } } diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 0ebc8ea86d..c56332f5f5 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -43,6 +43,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.ExtendWith; + import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Range.Bound; @@ -53,6 +54,7 @@ import org.springframework.data.redis.connection.BitFieldSubCommands; import org.springframework.data.redis.connection.ClusterConnectionTests; import org.springframework.data.redis.connection.ClusterSlotHashUtil; +import org.springframework.data.redis.connection.ClusterTopology; import org.springframework.data.redis.connection.DataType; import org.springframework.data.redis.connection.DefaultSortParameters; import org.springframework.data.redis.connection.Limit; @@ -75,6 +77,7 @@ import org.springframework.data.redis.test.condition.EnabledOnRedisClusterAvailable; import org.springframework.data.redis.test.extension.JedisExtension; import org.springframework.data.redis.test.util.HexStringUtils; +import org.springframework.test.util.ReflectionTestUtils; /** * @author Christoph Strobl @@ -2950,4 +2953,20 @@ void lPosNonExisting() { assertThat(result).isEmpty(); } + + @Test // GH-2986 + void shouldUseCachedTopology() { + + JedisClusterConnection.JedisClusterTopologyProvider provider = (JedisClusterConnection.JedisClusterTopologyProvider) clusterConnection + .getTopologyProvider(); + ReflectionTestUtils.setField(provider, "cached", null); + + ClusterTopology topology = provider.getTopology(); + assertThat(topology).isInstanceOf(JedisClusterConnection.JedisClusterTopology.class); + + assertThat(provider.shouldUseCachedValue(null)).isFalse(); + assertThat(provider.shouldUseCachedValue(new JedisClusterConnection.JedisClusterTopology(Set.of(), 0))).isFalse(); + assertThat(provider.shouldUseCachedValue( + new JedisClusterConnection.JedisClusterTopology(Set.of(), System.currentTimeMillis() + 100))).isTrue(); + } } From fd31e433c090c1256c47f07ea19337287ec5336c Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 11 Sep 2024 11:59:30 +0200 Subject: [PATCH 060/187] Polishing. Move maxTime calculation for topology refresh to and delegate shouldUseCachedValue() to newly introduced overload. Original Pull Request: #2989 --- .../jedis/JedisClusterConnection.java | 29 ++++++++++++++----- .../jedis/JedisClusterConnectionTests.java | 4 +-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java index 625c085729..b4b2d8422b 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java @@ -854,9 +854,8 @@ public ClusterTopology getTopology() { try (Connection connection = entry.getValue().getResource()) { - Set nodes = Converters.toSetOfRedisClusterNodes(new Jedis(connection).clusterNodes()); - topology = cached = new JedisClusterTopology(nodes, System.currentTimeMillis()); + topology = cached = new JedisClusterTopology(nodes, System.currentTimeMillis(), cacheTimeMs); return topology; } catch (Exception ex) { @@ -884,9 +883,9 @@ public ClusterTopology getTopology() { * @since 2.2 * @deprecated since 3.3.4, use {@link #shouldUseCachedValue(JedisClusterTopology)} instead. */ - @Deprecated(since = "3.3.4") + @Deprecated(since = "3.3.4", forRemoval = true) protected boolean shouldUseCachedValue() { - return false; + return shouldUseCachedValue(cached); } /** @@ -899,22 +898,38 @@ protected boolean shouldUseCachedValue() { * @since 3.3.4 */ protected boolean shouldUseCachedValue(@Nullable JedisClusterTopology topology) { - return topology != null && topology.getTime() + cacheTimeMs > System.currentTimeMillis(); + return topology != null && topology.getMaxTime() > System.currentTimeMillis(); } } protected static class JedisClusterTopology extends ClusterTopology { private final long time; + private final long timeoutMs; - public JedisClusterTopology(Set nodes, long time) { + JedisClusterTopology(Set nodes, long creationTimeMs, long timeoutMs) { super(nodes); - this.time = time; + this.time = creationTimeMs; + this.timeoutMs = timeoutMs; } + /** + * Get the time in ms when the {@link ClusterTopology} was captured. + * + * @return ClusterTopology time. + */ public long getTime() { return time; } + + /** + * Get the maximum time in ms the {@link ClusterTopology} should be used before a refresh is required. + * + * @return ClusterTopology maximum age. + */ + long getMaxTime() { + return time + timeoutMs; + } } protected JedisCluster getCluster() { diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index c56332f5f5..8ec56d8b36 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -2965,8 +2965,8 @@ void shouldUseCachedTopology() { assertThat(topology).isInstanceOf(JedisClusterConnection.JedisClusterTopology.class); assertThat(provider.shouldUseCachedValue(null)).isFalse(); - assertThat(provider.shouldUseCachedValue(new JedisClusterConnection.JedisClusterTopology(Set.of(), 0))).isFalse(); + assertThat(provider.shouldUseCachedValue(new JedisClusterConnection.JedisClusterTopology(Set.of(), System.currentTimeMillis() - 101, 100))).isFalse(); assertThat(provider.shouldUseCachedValue( - new JedisClusterConnection.JedisClusterTopology(Set.of(), System.currentTimeMillis() + 100))).isTrue(); + new JedisClusterConnection.JedisClusterTopology(Set.of(), System.currentTimeMillis() + 100, 100))).isTrue(); } } From 8777cf7fc69b63253af1be99073a6cfdbcdde14c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 11 Sep 2024 14:10:50 +0200 Subject: [PATCH 061/187] Fix database selection on dedicated connection. We now select the database on the dedicated connection. Previously, this call never happened on the dedicated connection and the only way a database could be selected is through the ConnectionFactory configuration. Closes: #2984 Original Pull Request: #2990 --- .../springframework/data/redis/RedisSystemException.java | 3 ++- .../data/redis/connection/lettuce/LettuceConnection.java | 8 ++++---- .../connection/lettuce/LettuceConnectionUnitTests.java | 7 ++++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/RedisSystemException.java b/src/main/java/org/springframework/data/redis/RedisSystemException.java index 50d2218dd3..0767a37698 100644 --- a/src/main/java/org/springframework/data/redis/RedisSystemException.java +++ b/src/main/java/org/springframework/data/redis/RedisSystemException.java @@ -16,6 +16,7 @@ package org.springframework.data.redis; import org.springframework.dao.UncategorizedDataAccessException; +import org.springframework.lang.Nullable; /** * Exception thrown when we can't classify a Redis exception into one of Spring generic data access exceptions. @@ -28,7 +29,7 @@ public class RedisSystemException extends UncategorizedDataAccessException { * @param msg the detail message. * @param cause the root cause from the data access API in use. */ - public RedisSystemException(String msg, Throwable cause) { + public RedisSystemException(String msg, @Nullable Throwable cause) { super(msg, cause); } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java index 0016e73a4c..2a7a79183f 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java @@ -512,7 +512,7 @@ private void reset() { if (this.asyncDedicatedConnection != null) { try { if (customizedDatabaseIndex()) { - potentiallySelectDatabase(this.defaultDbIndex); + potentiallySelectDatabase(this.asyncDedicatedConnection, this.defaultDbIndex); } this.connectionProvider.release(this.asyncDedicatedConnection); this.asyncDedicatedConnection = null; @@ -965,7 +965,7 @@ protected StatefulConnection doGetAsyncDedicatedConnection() { StatefulConnection connection = getConnectionProvider().getConnection(StatefulConnection.class); if (customizedDatabaseIndex()) { - potentiallySelectDatabase(this.dbIndex); + potentiallySelectDatabase(connection, this.dbIndex); } return connection; @@ -1062,9 +1062,9 @@ private boolean customizedDatabaseIndex() { return defaultDbIndex != dbIndex; } - private void potentiallySelectDatabase(int dbIndex) { + private static void potentiallySelectDatabase(StatefulConnection connection, int dbIndex) { - if (asyncDedicatedConnection instanceof StatefulRedisConnection statefulConnection) { + if (connection instanceof StatefulRedisConnection statefulConnection) { statefulConnection.sync().select(dbIndex); } } diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java index 0a6c19a166..e56af1cb3b 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java @@ -21,6 +21,7 @@ import io.lettuce.core.*; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.api.sync.RedisCommands; import io.lettuce.core.codec.ByteArrayCodec; import io.lettuce.core.codec.RedisCodec; import io.lettuce.core.codec.StringCodec; @@ -68,6 +69,7 @@ public static class BasicUnitTests extends AbstractConnectionUnitTestBase statefulConnectionMock; RedisAsyncCommands asyncCommandsMock; + RedisCommands commandsMock; @SuppressWarnings({ "unchecked" }) @BeforeEach @@ -89,8 +91,10 @@ public void setUp() throws InvocationTargetException, IllegalAccessException { } return null; }); + commandsMock = Mockito.mock(RedisCommands.class); when(statefulConnectionMock.async()).thenReturn(asyncCommandsMock); + when(statefulConnectionMock.sync()).thenReturn(commandsMock); connection = new LettuceConnection(0, clientMock); } @@ -155,7 +159,7 @@ void shouldThrowExceptionWhenAccessingRedisSentinelsCommandsWhenNoSentinelsConfi .isThrownBy(() -> connection.getSentinelConnection()); } - @Test // DATAREDIS-431 + @Test // DATAREDIS-431, GH-2984 void dbIndexShouldBeSetWhenObtainingConnection() { connection = new LettuceConnection(null, 0, clientMock, 0); @@ -163,6 +167,7 @@ void dbIndexShouldBeSetWhenObtainingConnection() { connection.getNativeConnection(); verify(asyncCommandsMock).dispatch(eq(CommandType.SELECT), any(), any()); + verify(commandsMock).select(1); } @Test // DATAREDIS-603 From feda7dcf5b0a2f451d8ad8b2b45c087c1af92bce Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 12 Sep 2024 13:26:17 +0200 Subject: [PATCH 062/187] Polishing. Make sure nested LettuceConnectionUnitTests are actually run by junit. Original Pull Request: #2990 --- .../lettuce/LettuceConnectionUnitTests.java | 130 ++++++++++-------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java index e56af1cb3b..612367c06c 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java @@ -15,10 +15,29 @@ */ package org.springframework.data.redis.connection.lettuce; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import io.lettuce.core.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyMap; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.lettuce.core.KeyScanCursor; +import io.lettuce.core.MapScanCursor; +import io.lettuce.core.RedisClient; +import io.lettuce.core.RedisFuture; +import io.lettuce.core.ScanArgs; +import io.lettuce.core.ScanCursor; +import io.lettuce.core.ScoredValue; +import io.lettuce.core.ScoredValueScanCursor; +import io.lettuce.core.ValueScanCursor; +import io.lettuce.core.XAddArgs; +import io.lettuce.core.XClaimArgs; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.async.RedisAsyncCommands; import io.lettuce.core.api.sync.RedisCommands; @@ -42,6 +61,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -60,43 +80,44 @@ * @author Christoph Strobl * @author Mark Paluch */ -public class LettuceConnectionUnitTests { +class LettuceConnectionUnitTests { - @SuppressWarnings("rawtypes") - public static class BasicUnitTests extends AbstractConnectionUnitTestBase { + protected LettuceConnection connection; + private RedisClient clientMock; + StatefulRedisConnection statefulConnectionMock; + RedisAsyncCommands asyncCommandsMock; + RedisCommands commandsMock; - protected LettuceConnection connection; - private RedisClient clientMock; - StatefulRedisConnection statefulConnectionMock; - RedisAsyncCommands asyncCommandsMock; - RedisCommands commandsMock; + @BeforeEach + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void setUp() throws InvocationTargetException, IllegalAccessException { - @SuppressWarnings({ "unchecked" }) - @BeforeEach - public void setUp() throws InvocationTargetException, IllegalAccessException { + clientMock = mock(RedisClient.class); + statefulConnectionMock = mock(StatefulRedisConnection.class); + when(clientMock.connect((RedisCodec) any())).thenReturn(statefulConnectionMock); - clientMock = mock(RedisClient.class); - statefulConnectionMock = mock(StatefulRedisConnection.class); - when(clientMock.connect((RedisCodec) any())).thenReturn(statefulConnectionMock); + asyncCommandsMock = Mockito.mock(RedisAsyncCommands.class, invocation -> { - asyncCommandsMock = Mockito.mock(RedisAsyncCommands.class, invocation -> { + if (invocation.getMethod().getReturnType().equals(RedisFuture.class)) { - if (invocation.getMethod().getReturnType().equals(RedisFuture.class)) { + Command cmd = new Command<>(CommandType.PING, new StatusOutput<>(StringCodec.UTF8)); + AsyncCommand async = new AsyncCommand<>(cmd); + async.complete(); - Command cmd = new Command<>(CommandType.PING, new StatusOutput<>(StringCodec.UTF8)); - AsyncCommand async = new AsyncCommand<>(cmd); - async.complete(); + return async; + } + return null; + }); + commandsMock = Mockito.mock(RedisCommands.class); - return async; - } - return null; - }); - commandsMock = Mockito.mock(RedisCommands.class); + when(statefulConnectionMock.async()).thenReturn(asyncCommandsMock); + when(statefulConnectionMock.sync()).thenReturn(commandsMock); + connection = new LettuceConnection(0, clientMock); + } - when(statefulConnectionMock.async()).thenReturn(asyncCommandsMock); - when(statefulConnectionMock.sync()).thenReturn(commandsMock); - connection = new LettuceConnection(0, clientMock); - } + @Nested + @SuppressWarnings({ "rawtypes", "deprecation" }) + class BasicUnitTests extends AbstractConnectionUnitTestBase { @Test // DATAREDIS-184 public void shutdownWithNullOptionsIsCalledCorrectly() { @@ -178,8 +199,7 @@ void translatesUnknownExceptions() { when(asyncCommandsMock.set(any(), any())).thenThrow(exception); connection = new LettuceConnection(null, 0, clientMock, 1); - assertThatThrownBy(() -> connection.set("foo".getBytes(), "bar".getBytes())) - .hasRootCause(exception); + assertThatThrownBy(() -> connection.set("foo".getBytes(), "bar".getBytes())).hasRootCause(exception); } @Test // DATAREDIS-603 @@ -270,8 +290,8 @@ public List getKeys() { sc.setCursor(cursorId); sc.setFinished(false); - Command> command = new Command<>(new LettuceConnection.CustomCommandType("SCAN"), - new ScanOutput<>(ByteArrayCodec.INSTANCE, sc) { + Command> command = new Command<>( + new LettuceConnection.CustomCommandType("SCAN"), new ScanOutput<>(ByteArrayCodec.INSTANCE, sc) { @Override protected void setOutput(ByteBuffer bytes) { @@ -280,10 +300,10 @@ protected void setOutput(ByteBuffer bytes) { AsyncCommand> future = new AsyncCommand<>(command); future.complete(); - when(asyncCommandsMock.scan(any(ScanCursor.class),any(ScanArgs.class))).thenReturn(future, future); + when(asyncCommandsMock.scan(any(ScanCursor.class), any(ScanArgs.class))).thenReturn(future, future); Cursor cursor = connection.scan(KeyScanOptions.NONE); - cursor.next(); //initial + cursor.next(); // initial assertThat(cursor.getCursorId()).isEqualTo(Long.parseUnsignedLong(cursorId)); cursor.next(); // fetch next @@ -305,8 +325,8 @@ public List getValues() { sc.setCursor(cursorId); sc.setFinished(false); - Command> command = new Command<>(new LettuceConnection.CustomCommandType("SSCAN"), - new ScanOutput<>(ByteArrayCodec.INSTANCE, sc) { + Command> command = new Command<>( + new LettuceConnection.CustomCommandType("SSCAN"), new ScanOutput<>(ByteArrayCodec.INSTANCE, sc) { @Override protected void setOutput(ByteBuffer bytes) { @@ -315,10 +335,11 @@ protected void setOutput(ByteBuffer bytes) { AsyncCommand> future = new AsyncCommand<>(command); future.complete(); - when(asyncCommandsMock.sscan(any(byte[].class), any(ScanCursor.class),any(ScanArgs.class))).thenReturn(future, future); + when(asyncCommandsMock.sscan(any(byte[].class), any(ScanCursor.class), any(ScanArgs.class))).thenReturn(future, + future); Cursor cursor = connection.setCommands().sScan("key".getBytes(), KeyScanOptions.NONE); - cursor.next(); //initial + cursor.next(); // initial assertThat(cursor.getCursorId()).isEqualTo(Long.parseUnsignedLong(cursorId)); cursor.next(); // fetch next @@ -340,8 +361,8 @@ public List> getValues() { sc.setCursor(cursorId); sc.setFinished(false); - Command> command = new Command<>(new LettuceConnection.CustomCommandType("ZSCAN"), - new ScanOutput<>(ByteArrayCodec.INSTANCE, sc) { + Command> command = new Command<>( + new LettuceConnection.CustomCommandType("ZSCAN"), new ScanOutput<>(ByteArrayCodec.INSTANCE, sc) { @Override protected void setOutput(ByteBuffer bytes) { @@ -350,10 +371,11 @@ protected void setOutput(ByteBuffer bytes) { AsyncCommand> future = new AsyncCommand<>(command); future.complete(); - when(asyncCommandsMock.zscan(any(byte[].class), any(ScanCursor.class),any(ScanArgs.class))).thenReturn(future, future); + when(asyncCommandsMock.zscan(any(byte[].class), any(ScanCursor.class), any(ScanArgs.class))).thenReturn(future, + future); Cursor cursor = connection.zSetCommands().zScan("key".getBytes(), KeyScanOptions.NONE); - cursor.next(); //initial + cursor.next(); // initial assertThat(cursor.getCursorId()).isEqualTo(Long.parseUnsignedLong(cursorId)); cursor.next(); // fetch next @@ -375,8 +397,8 @@ public Map getMap() { sc.setCursor(cursorId); sc.setFinished(false); - Command> command = new Command<>(new LettuceConnection.CustomCommandType("HSCAN"), - new ScanOutput<>(ByteArrayCodec.INSTANCE, sc) { + Command> command = new Command<>( + new LettuceConnection.CustomCommandType("HSCAN"), new ScanOutput<>(ByteArrayCodec.INSTANCE, sc) { @Override protected void setOutput(ByteBuffer bytes) { @@ -385,10 +407,11 @@ protected void setOutput(ByteBuffer bytes) { AsyncCommand> future = new AsyncCommand<>(command); future.complete(); - when(asyncCommandsMock.hscan(any(byte[].class), any(ScanCursor.class),any(ScanArgs.class))).thenReturn(future, future); + when(asyncCommandsMock.hscan(any(byte[].class), any(ScanCursor.class), any(ScanArgs.class))).thenReturn(future, + future); Cursor> cursor = connection.hashCommands().hScan("key".getBytes(), KeyScanOptions.NONE); - cursor.next(); //initial + cursor.next(); // initial assertThat(cursor.getCursorId()).isEqualTo(Long.parseUnsignedLong(cursorId)); cursor.next(); // fetch next @@ -399,13 +422,12 @@ protected void setOutput(ByteBuffer bytes) { } - public static class LettucePipelineConnectionUnitTests extends BasicUnitTests { + @Nested + class LettucePipelineConnectionUnitTests extends BasicUnitTests { - @Override @BeforeEach public void setUp() throws InvocationTargetException, IllegalAccessException { - super.setUp(); - this.connection.openPipeline(); + connection.openPipeline(); } @Test // DATAREDIS-528 From f2a1af6cbb434aad55b74b5c267fa308abc9b18b Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 13 Sep 2024 09:38:22 +0200 Subject: [PATCH 063/187] Make sure db index is only set once. See: #2984 --- .../data/redis/connection/lettuce/LettuceConnection.java | 9 +-------- .../connection/lettuce/LettuceConnectionUnitTests.java | 3 ++- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java index 2a7a79183f..aca9bb7a2b 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java @@ -961,14 +961,7 @@ protected RedisClusterAsyncCommands getAsyncDedicatedConnection( @SuppressWarnings("unchecked") protected StatefulConnection doGetAsyncDedicatedConnection() { - - StatefulConnection connection = getConnectionProvider().getConnection(StatefulConnection.class); - - if (customizedDatabaseIndex()) { - potentiallySelectDatabase(connection, this.dbIndex); - } - - return connection; + return getConnectionProvider().getConnection(StatefulConnection.class); } @Override diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java index 612367c06c..5dd0eb13c1 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import io.lettuce.core.KeyScanCursor; @@ -188,7 +189,7 @@ void dbIndexShouldBeSetWhenObtainingConnection() { connection.getNativeConnection(); verify(asyncCommandsMock).dispatch(eq(CommandType.SELECT), any(), any()); - verify(commandsMock).select(1); + verifyNoInteractions(commandsMock); } @Test // DATAREDIS-603 From 108bd781da91c0ce847f7fe7c1dd27bccc267624 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 13 Sep 2024 12:39:15 +0200 Subject: [PATCH 064/187] Prepare 3.4 M1 (2024.1.0). See #2912 --- pom.xml | 16 +++------------- src/main/resources/notice.txt | 3 ++- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 09bebeb5bd..3915a94944 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.4.0-SNAPSHOT + 3.4.0-M1 - 3.4.0-SNAPSHOT - 3.4.0-SNAPSHOT + 3.4.0-M1 + 3.4.0-M1 4.0.2 1.9.4 1.4.20 @@ -376,16 +376,6 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - spring-milestone https://repo.spring.io/milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index b33d38469b..a546af9c6d 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 3.3 GA (2024.0.0) +Spring Data Redis 3.4 M1 (2024.1.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -53,5 +53,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From 7e434ec318febe48a0db2bc14453821eb3e42496 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 13 Sep 2024 12:39:33 +0200 Subject: [PATCH 065/187] Release version 3.4 M1 (2024.1.0). See #2912 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3915a94944..6b4e5705a6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.4.0-SNAPSHOT + 3.4.0-M1 Spring Data Redis Spring Data module for Redis From 55360bb6dee84cf97a7f07128b00d6b728270d68 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 13 Sep 2024 12:42:27 +0200 Subject: [PATCH 066/187] Prepare next development iteration. See #2912 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b4e5705a6..3915a94944 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.4.0-M1 + 3.4.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From 5a1c1ef992a2886e7db1b933eb4af85719d2262f Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 13 Sep 2024 12:42:28 +0200 Subject: [PATCH 067/187] After release cleanups. See #2912 --- pom.xml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 3915a94944..09bebeb5bd 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.4.0-M1 + 3.4.0-SNAPSHOT - 3.4.0-M1 - 3.4.0-M1 + 3.4.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.2 1.9.4 1.4.20 @@ -376,6 +376,16 @@ + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + spring-milestone https://repo.spring.io/milestone From 078f6baaa030011aaeb4920826083d97829e8368 Mon Sep 17 00:00:00 2001 From: jinbeom Date: Sun, 15 Sep 2024 08:19:15 +0900 Subject: [PATCH 068/187] Remove duplicate code in `DefaultRedisList`. Closes: #2996 Original pull request: #2997 --- .../support/collections/DefaultRedisList.java | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java index 8c9aa5f8d0..b2ea1d878e 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java @@ -41,6 +41,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author John Blum + * @author Jinbeom Kim */ public class DefaultRedisList extends AbstractRedisCollection implements RedisList { @@ -216,16 +217,14 @@ public boolean remove(Object o) { public void add(int index, E element) { if (index == 0) { - listOps.leftPush(element); - cap(); + addFirst(element); return; } int size = size(); if (index == size()) { - listOps.rightPush(element); - cap(); + addLast(element); return; } @@ -241,24 +240,15 @@ public boolean addAll(int index, Collection collection) { // insert collection in reverse if (index == 0) { - - Collection reverseCollection = CollectionUtils.reverse(collection); - - for (E element : reverseCollection) { - listOps.leftPush(element); - cap(); - } - + CollectionUtils.reverse(collection) + .forEach(this::addFirst); return true; } int size = size(); if (index == size()) { - for (E element : collection) { - listOps.rightPush(element); - cap(); - } + collection.forEach(this::addLast); return true; } @@ -341,9 +331,7 @@ public E element() { @Override public boolean offer(E element) { - listOps.rightPush(element); - cap(); - return true; + return add(element); } @Override From 7dd8b8ed5d70974f4d8f1012d11dc29e679df56e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 17 Sep 2024 14:20:47 +0200 Subject: [PATCH 069/187] Polishing. Add missing Nullable annotations. Reorder methods. See: #2996 Original pull request: #2997 --- .../data/redis/core/BoundListOperations.java | 2 ++ .../collections/AbstractRedisCollection.java | 14 +++++++------- .../redis/support/collections/DefaultRedisMap.java | 11 +++++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java index 2e76833c39..02d4f53091 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java @@ -249,6 +249,7 @@ public interface BoundListOperations extends BoundKeyOperations { * @since 2.4 * @see Redis Documentation: LPOS */ + @Nullable Long indexOf(V value); /** @@ -260,6 +261,7 @@ public interface BoundListOperations extends BoundKeyOperations { * @since 2.4 * @see Redis Documentation: LPOS */ + @Nullable Long lastIndexOf(V value); /** diff --git a/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java b/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java index 842905a305..7f0a26f92b 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java +++ b/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java @@ -131,13 +131,6 @@ public void rename(final String newKey) { key = newKey; } - protected void checkResult(@Nullable Object obj) { - - if (obj == null) { - throw new IllegalStateException("Cannot read collection with Redis connection in pipeline/multi-exec mode"); - } - } - @Override public boolean equals(@Nullable Object o) { @@ -168,4 +161,11 @@ public int hashCode() { public String toString() { return "%s for key: %s".formatted(getClass().getSimpleName(), getKey()); } + + protected void checkResult(@Nullable Object obj) { + + if (obj == null) { + throw new IllegalStateException("Cannot read collection with Redis connection in pipeline/multi-exec mode"); + } + } } diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java index d5cb4659ed..93fd9cbed1 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java @@ -187,7 +187,6 @@ public int hashCode() { @Override public String toString() { - return "RedisStore for key:" + getKey(); } @@ -317,17 +316,17 @@ public DataType getType() { return hashOps.getType(); } + @Override + public Cursor> scan() { + return scan(ScanOptions.NONE); + } + private void checkResult(@Nullable Object obj) { if (obj == null) { throw new IllegalStateException("Cannot read collection with Redis connection in pipeline/multi-exec mode"); } } - @Override - public Cursor> scan() { - return scan(ScanOptions.NONE); - } - /** * @since 1.4 * @param options From 42b1764667d5c832483f4b7576ed23fc05af3548 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 17 Sep 2024 14:54:23 +0200 Subject: [PATCH 070/187] Return cached `HashOperations` instance from `opsForHash`. Closes #2970 --- .../org/springframework/data/redis/core/RedisTemplate.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java index 93ee4ca0de..d4585da005 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java @@ -116,6 +116,7 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation ObjectHashMapper.getSharedInstance()); private final ZSetOperations zSetOps = new DefaultZSetOperations<>(this); private final GeoOperations geoOps = new DefaultGeoOperations<>(this); + private final HashOperations hashOps = new DefaultHashOperations<>(this); private final HyperLogLogOperations hllOps = new DefaultHyperLogLogOperations<>(this); private final ClusterOperations clusterOps = new DefaultClusterOperations<>(this); @@ -977,7 +978,7 @@ public BoundHashOperations boundHashOps(K key) { @Override public HashOperations opsForHash() { - return new DefaultHashOperations<>(this); + return (HashOperations) hashOps; } @Override From 8906eb57bb0c21eb43c9b549a4fff094a7a5a67c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 17 Sep 2024 14:54:55 +0200 Subject: [PATCH 071/187] Polishing. Reword Javadoc comments for clarity and add missing `@SuppressWarnings` annotations. See #2970 --- .../data/redis/core/RedisOperations.java | 2 +- .../data/redis/core/RedisTemplate.java | 65 +++++++++++++------ 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java index 7128522447..0869015a8f 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java @@ -650,7 +650,7 @@ default void restore(K key, byte[] value, long timeToLive, TimeUnit unit) { BoundHashOperations boundHashOps(K key); /** - * @return + * @return never {@literal null}. * @since 1.5 */ HyperLogLogOperations opsForHyperLogLog(); diff --git a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java index d4585da005..3afcf1f253 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java @@ -66,10 +66,11 @@ * Redis store. By default, it uses Java serialization for its objects (through {@link JdkSerializationRedisSerializer} * ). For String intensive operations consider the dedicated {@link StringRedisTemplate}. *

- * The central method is execute, supporting Redis access code implementing the {@link RedisCallback} interface. It - * provides {@link RedisConnection} handling such that neither the {@link RedisCallback} implementation nor the calling - * code needs to explicitly care about retrieving/closing Redis connections, or handling Connection lifecycle - * exceptions. For typical single step actions, there are various convenience methods. + * The central method is {@link #execute(RedisCallback)}, supporting Redis access code implementing the + * {@link RedisCallback} interface. It provides {@link RedisConnection} handling such that neither the + * {@link RedisCallback} implementation nor the calling code needs to explicitly care about retrieving/closing Redis + * connections, or handling Connection lifecycle exceptions. For typical single step actions, there are various + * convenience methods. *

* Once configured, this class is thread-safe. *

@@ -121,7 +122,7 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation private final ClusterOperations clusterOps = new DefaultClusterOperations<>(this); /** - * Constructs a new RedisTemplate instance. + * Constructs a new {@code RedisTemplate} instance. */ public RedisTemplate() {} @@ -160,46 +161,59 @@ public void afterPropertiesSet() { } /** - * Returns whether to expose the native Redis connection to RedisCallback code, or rather a connection proxy (the - * default). + * Returns whether the underlying RedisConnection should be directly exposed to the RedisCallback code, or rather a + * connection proxy (default behavior). * - * @return whether to expose the native Redis connection or not + * @return {@literal true} to expose the native Redis connection or {@literal false} to provide a proxied connection + * to RedisCallback code. */ public boolean isExposeConnection() { return exposeConnection; } /** - * Sets whether to expose the Redis connection to {@link RedisCallback} code. Default is "false": a proxy will be - * returned, suppressing {@code quit} and {@code disconnect} calls. + * Sets whether the underlying RedisConnection should be directly exposed to the RedisCallback code. By default, the + * connection is not exposed, and a proxy is used instead. This proxy suppresses potentially disruptive operations, + * such as {@code quit} and {@code disconnect} commands, ensuring that the connection remains stable during the + * callback execution. Defaults to proxy use. * - * @param exposeConnection + * @param exposeConnection {@literal true} to expose the actual Redis connection to RedisCallback code, allowing full + * access to Redis commands, including quit and disconnect. {@literal false} to proxy connections that + * suppress the quit and disconnect commands, protecting the connection from being inadvertently closed + * during callback execution. */ public void setExposeConnection(boolean exposeConnection) { this.exposeConnection = exposeConnection; } /** - * @return Whether or not the default serializer should be used. If not, any serializers not explicitly set will - * remain null and values will not be serialized or deserialized. + * Returns whether the default serializer should be used or not. + * + * @return {@literal true} if the default serializer should be used; {@literal false} otherwise. */ public boolean isEnableDefaultSerializer() { return enableDefaultSerializer; } /** - * @param enableDefaultSerializer Whether or not the default serializer should be used. If not, any serializers not - * explicitly set will remain null and values will not be serialized or deserialized. + * Configure whether the default serializer should be used or not. If the default serializer is enabled, the template + * will use it to serialize and deserialize values. However, if the default serializer is disabled , any serializers + * that have not been explicitly set will remain {@literal null}, and their corresponding values will neither be + * serialized nor deserialized. Defaults to {@literal true}. + * + * @param enableDefaultSerializer {@literal true} if the default serializer should be used; {@literal false} + * otherwise. */ public void setEnableDefaultSerializer(boolean enableDefaultSerializer) { this.enableDefaultSerializer = enableDefaultSerializer; } /** - * If set to {@code true} {@link RedisTemplate} will participate in ongoing transactions using - * {@literal MULTI...EXEC|DISCARD} to keep track of operations. + * Sets whether this template participates in ongoing transactions using {@literal MULTI...EXEC|DISCARD} to keep track + * of operations. * - * @param enableTransactionSupport whether to participate in ongoing transactions. + * @param enableTransactionSupport {@literal true}to participate in ongoing transactions; {@literal false} to not + * track transactions. * @since 1.3 * @see RedisConnectionUtils#getConnection(RedisConnectionFactory, boolean) * @see TransactionSynchronizationManager#isActualTransactionActive() @@ -209,7 +223,7 @@ public void setEnableTransactionSupport(boolean enableTransactionSupport) { } /** - * Set the {@link ClassLoader} to be used for the default {@link JdkSerializationRedisSerializer} in case no other + * Sets the {@link ClassLoader} to be used for the default {@link JdkSerializationRedisSerializer} in case no other * {@link RedisSerializer} is explicitly set as the default one. * * @param classLoader can be {@literal null}. @@ -224,7 +238,7 @@ public void setBeanClassLoader(ClassLoader classLoader) { /** * Returns the default serializer used by this template. * - * @return template default serializer + * @return template default serializer. */ @Nullable public RedisSerializer getDefaultSerializer() { @@ -236,7 +250,7 @@ public RedisSerializer getDefaultSerializer() { * {@link #setStringSerializer(RedisSerializer)}) are initialized to this value unless explicitly set. Defaults to * {@link JdkSerializationRedisSerializer}. * - * @param serializer default serializer to use + * @param serializer default serializer to use. */ public void setDefaultSerializer(RedisSerializer serializer) { this.defaultSerializer = serializer; @@ -967,16 +981,19 @@ public GeoOperations opsForGeo() { } @Override + @SuppressWarnings("unchecked") public BoundGeoOperations boundGeoOps(K key) { return boundOperations.createProxy(BoundGeoOperations.class, key, DataType.ZSET, this, RedisOperations::opsForGeo); } @Override + @SuppressWarnings("unchecked") public BoundHashOperations boundHashOps(K key) { return boundOperations.createProxy(BoundHashOperations.class, key, DataType.HASH, this, it -> it.opsForHash()); } @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) public HashOperations opsForHash() { return (HashOperations) hashOps; } @@ -992,12 +1009,14 @@ public ListOperations opsForList() { } @Override + @SuppressWarnings("unchecked") public BoundListOperations boundListOps(K key) { return boundOperations.createProxy(BoundListOperations.class, key, DataType.LIST, this, RedisOperations::opsForList); } @Override + @SuppressWarnings("unchecked") public BoundSetOperations boundSetOps(K key) { return boundOperations.createProxy(BoundSetOperations.class, key, DataType.SET, this, RedisOperations::opsForSet); } @@ -1008,6 +1027,7 @@ public SetOperations opsForSet() { } @Override + @SuppressWarnings("unchecked") public StreamOperations opsForStream() { return (StreamOperations) streamOps; } @@ -1018,11 +1038,13 @@ public StreamOperations opsForStream(HashMapper BoundStreamOperations boundStreamOps(K key) { return boundOperations.createProxy(BoundStreamOperations.class, key, DataType.STREAM, this, it -> opsForStream()); } @Override + @SuppressWarnings("unchecked") public BoundValueOperations boundValueOps(K key) { return boundOperations.createProxy(BoundValueOperations.class, key, DataType.STRING, this, RedisOperations::opsForValue); @@ -1034,6 +1056,7 @@ public ValueOperations opsForValue() { } @Override + @SuppressWarnings("unchecked") public BoundZSetOperations boundZSetOps(K key) { return boundOperations.createProxy(BoundZSetOperations.class, key, DataType.ZSET, this, RedisOperations::opsForZSet); From 53709f0281ed317985b2496ae1fe6661cec250d4 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 18 Sep 2024 14:35:59 +0200 Subject: [PATCH 072/187] Improve Cluster Nodes parsing. Without this fix there's a problem with parsing of the cluster nodes command output (e.g. a trailing comma after cport is making the parsing fail) with this change we're converting regexp parsing to code parsing which also includes support for trailing commas after cport Closes #2862 Original Pull Request: #3000 --- .../redis/connection/convert/Converters.java | 84 ++++++++++++++++--- .../convert/ConvertersUnitTests.java | 48 +++++++++-- 2 files changed, 115 insertions(+), 17 deletions(-) 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 b0ef79f896..107ed7891e 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 @@ -20,11 +20,10 @@ import java.time.Duration; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.convert.converter.Converter; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResult; @@ -45,6 +44,7 @@ import org.springframework.data.redis.connection.zset.Tuple; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.util.ByteUtils; +import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -545,10 +545,15 @@ enum ClusterNodesConverter implements Converter { *

  • {@code %s:%i} (Redis 3)
  • *
  • {@code %s:%i@%i} (Redis 4, with bus port)
  • *
  • {@code %s:%i@%i,%s} (Redis 7, with announced hostname)
  • + * + * The output of the {@code CLUSTER NODES } command is just a space-separated CSV string, where each + * line represents a node in the cluster. The following is an example of output on Redis 7.2.0. + * You can check the latest here. + * + * {@code ... } + * * */ - static final Pattern clusterEndpointPattern = Pattern - .compile("\\[?([0-9a-zA-Z\\-_\\.:]*)\\]?:([0-9]+)(?:@[0-9]+(?:,([^,].*))?)?"); private static final Map flagLookupMap; static { @@ -567,18 +572,75 @@ enum ClusterNodesConverter implements Converter { static final int LINK_STATE_INDEX = 7; static final int SLOTS_INDEX = 8; + record AddressPortHostname(String addressPart, String portPart, @Nullable String hostnamePart) { + + static AddressPortHostname of(String[] args) { + Assert.isTrue(args.length >= HOST_PORT_INDEX + 1, "ClusterNode information does not define host and port"); + // + String hostPort = args[HOST_PORT_INDEX]; + int lastColon = hostPort.lastIndexOf(":"); + Assert.isTrue(lastColon != -1, "ClusterNode information does not define host and port"); + String addressPart = getAddressPart(hostPort, lastColon); + // Everything to the right of port + int indexOfColon = hostPort.indexOf(","); + boolean hasColon = indexOfColon != -1; + String hostnamePart = getHostnamePart(hasColon, hostPort, indexOfColon); + String portPart = getPortPart(hostPort, lastColon, hasColon, indexOfColon); + return new AddressPortHostname(addressPart, portPart, hostnamePart); + } + + @NonNull private static String getAddressPart(String hostPort, int lastColon) { + // Everything to the left of port + // 127.0.0.1:6380 + // 127.0.0.1:6380@6381 + // :6380 + // :6380@6381 + // 2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380 + // 2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380@6381 + // 127.0.0.1:6380,hostname1 + // 127.0.0.1:6380@6381,hostname1 + // :6380,hostname1 + // :6380@6381,hostname1 + // 2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380,hostname1 + // 2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380@6381,hostname1 + String addressPart = hostPort.substring(0, lastColon); + // [2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380 + // [2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380@6381 + // [2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380,hostname1 + // [2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380@6381,hostname1 + if (addressPart.startsWith("[") && addressPart.endsWith("]")) { + addressPart = addressPart.substring(1, addressPart.length() - 1); + } + return addressPart; + } + + @Nullable + private static String getHostnamePart(boolean hasColon, String hostPort, int indexOfColon) { + // Everything to the right starting from comma + String hostnamePart = hasColon ? hostPort.substring(indexOfColon + 1) : null; + return StringUtils.hasText(hostnamePart) ? hostnamePart : null; + } + + @NonNull private static String getPortPart(String hostPort, int lastColon, boolean hasColon, int indexOfColon) { + String portPart = hostPort.substring(lastColon + 1); + if (portPart.contains("@")) { + portPart = portPart.substring(0, portPart.indexOf("@")); + } else if (hasColon) { + portPart = portPart.substring(0, indexOfColon); + } + return portPart; + } + } + @Override public RedisClusterNode convert(String source) { String[] args = source.split(" "); - Matcher matcher = clusterEndpointPattern.matcher(args[HOST_PORT_INDEX]); - - Assert.isTrue(matcher.matches(), "ClusterNode information does not define host and port"); - - String addressPart = matcher.group(1); - String portPart = matcher.group(2); - String hostnamePart = matcher.group(3); + AddressPortHostname addressPortHostname = AddressPortHostname.of(args); + String addressPart = addressPortHostname.addressPart; + String portPart = addressPortHostname.portPart; + String hostnamePart = addressPortHostname.hostnamePart; SlotRange range = parseSlotRange(args); Set flags = parseFlags(args); diff --git a/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java b/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java index 74d3f7df8d..7bd64f2135 100644 --- a/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java @@ -18,9 +18,9 @@ import static org.assertj.core.api.Assertions.*; import java.util.Iterator; -import java.util.regex.Matcher; import java.util.stream.Stream; +import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -72,6 +72,10 @@ class ConvertersUnitTests { private static final String CLUSTER_NODE_WITH_SINGLE_INVALID_IPV6_HOST = "67adfe3df1058896e3cb49d2863e0f70e7e159fa 2a02:6b8:c67:9c:0:6d8b:33da:5a2c: master,nofailover - 0 1692108412315 1 connected 0-5460"; + private static final String CLUSTER_NODE_WITH_SINGLE_IPV4_EMPTY_HOSTNAME = "3765733728631672640db35fd2f04743c03119c6 10.180.0.33:11003@16379, master - 0 1708041426947 2 connected 0-5460"; + + private static final String CLUSTER_NODE_WITH_SINGLE_IPV4_HOSTNAME = "3765733728631672640db35fd2f04743c03119c6 10.180.0.33:11003@16379,hostname1 master - 0 1708041426947 2 connected 0-5460"; + @Test // DATAREDIS-315 void toSetOfRedis30ClusterNodesShouldConvertSingleStringNodesResponseCorrectly() { @@ -248,6 +252,39 @@ void toClusterNodeWithIPv6Hostname() { assertThat(node.getSlotRange().getSlots().size()).isEqualTo(5461); } + @Test // https://github.com/spring-projects/spring-data-redis/issues/2862 + void toClusterNodeWithIPv4EmptyHostname() { + RedisClusterNode node = Converters.toClusterNode(CLUSTER_NODE_WITH_SINGLE_IPV4_EMPTY_HOSTNAME); + + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(node.getId()).isEqualTo("3765733728631672640db35fd2f04743c03119c6"); + softAssertions.assertThat(node.getHost()).isEqualTo("10.180.0.33"); + softAssertions.assertThat(node.hasValidHost()).isTrue(); + softAssertions.assertThat(node.getPort()).isEqualTo(11003); + softAssertions.assertThat(node.getType()).isEqualTo(NodeType.MASTER); + softAssertions.assertThat(node.getFlags()).contains(Flag.MASTER); + softAssertions.assertThat(node.getLinkState()).isEqualTo(LinkState.CONNECTED); + softAssertions.assertThat(node.getSlotRange().getSlots().size()).isEqualTo(5461); + }); + } + + @Test // https://github.com/spring-projects/spring-data-redis/issues/2862 + void toClusterNodeWithIPv4Hostname() { + RedisClusterNode node = Converters.toClusterNode(CLUSTER_NODE_WITH_SINGLE_IPV4_HOSTNAME); + + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(node.getId()).isEqualTo("3765733728631672640db35fd2f04743c03119c6"); + softAssertions.assertThat(node.getHost()).isEqualTo("10.180.0.33"); + softAssertions.assertThat(node.getName()).isEqualTo("hostname1"); + softAssertions.assertThat(node.hasValidHost()).isTrue(); + softAssertions.assertThat(node.getPort()).isEqualTo(11003); + softAssertions.assertThat(node.getType()).isEqualTo(NodeType.MASTER); + softAssertions.assertThat(node.getFlags()).contains(Flag.MASTER); + softAssertions.assertThat(node.getLinkState()).isEqualTo(LinkState.CONNECTED); + softAssertions.assertThat(node.getSlotRange().getSlots().size()).isEqualTo(5461); + }); + } + @Test // GH-2678 void toClusterNodeWithIPv6HostnameSquareBrackets() { @@ -273,12 +310,11 @@ void toClusterNodeWithInvalidIPv6Hostname() { @MethodSource("clusterNodesEndpoints") void shouldAcceptHostPatterns(String endpoint, String expectedAddress, String expectedPort, String expectedHostname) { - Matcher matcher = ClusterNodesConverter.clusterEndpointPattern.matcher(endpoint); - assertThat(matcher.matches()).isTrue(); + ClusterNodesConverter.AddressPortHostname addressPortHostname = ClusterNodesConverter.AddressPortHostname.of(new String[] { "id", endpoint }); - assertThat(matcher.group(1)).isEqualTo(expectedAddress); - assertThat(matcher.group(2)).isEqualTo(expectedPort); - assertThat(matcher.group(3)).isEqualTo(expectedHostname); + assertThat(addressPortHostname.addressPart()).isEqualTo(expectedAddress); + assertThat(addressPortHostname.portPart()).isEqualTo(expectedPort); + assertThat(addressPortHostname.hostnamePart()).isEqualTo(expectedHostname); } static Stream clusterNodesEndpoints() { From f2752d1b029f0d2642df588ab2adca8b0110dee0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 19 Sep 2024 14:23:46 +0200 Subject: [PATCH 073/187] Polishing. Update tests to simplify assertions and enhance GH issue references. Simplify parsing logic for addressing edge cases and added more test scenarios. See #2862 Original Pull Request: #3000 --- .../redis/connection/convert/Converters.java | 121 +++++++++--------- .../convert/ConvertersUnitTests.java | 105 +++++++++------ 2 files changed, 126 insertions(+), 100 deletions(-) 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 107ed7891e..2dc686bcf9 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 @@ -44,7 +44,6 @@ import org.springframework.data.redis.connection.zset.Tuple; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.util.ByteUtils; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -63,6 +62,7 @@ * @author daihuabin * @author John Blum * @author Sorokin Evgeniy + * @author Marcin Grzejszczak */ public abstract class Converters { @@ -572,63 +572,62 @@ enum ClusterNodesConverter implements Converter { static final int LINK_STATE_INDEX = 7; static final int SLOTS_INDEX = 8; - record AddressPortHostname(String addressPart, String portPart, @Nullable String hostnamePart) { - - static AddressPortHostname of(String[] args) { - Assert.isTrue(args.length >= HOST_PORT_INDEX + 1, "ClusterNode information does not define host and port"); - // - String hostPort = args[HOST_PORT_INDEX]; - int lastColon = hostPort.lastIndexOf(":"); - Assert.isTrue(lastColon != -1, "ClusterNode information does not define host and port"); - String addressPart = getAddressPart(hostPort, lastColon); - // Everything to the right of port - int indexOfColon = hostPort.indexOf(","); - boolean hasColon = indexOfColon != -1; - String hostnamePart = getHostnamePart(hasColon, hostPort, indexOfColon); - String portPart = getPortPart(hostPort, lastColon, hasColon, indexOfColon); + /** + * Value object capturing Redis' representation of a cluster node network coordinate. + * + * @author Marcin Grzejszczak + * @author Mark Paluch + */ + record AddressPortHostname(String address, String port, @Nullable String hostname) { + + /** + * Parses Redis {@code CLUSTER NODES} host and port segment into {@link AddressPortHostname}. + */ + static AddressPortHostname parse(String hostAndPortPart) { + + String[] segments = hostAndPortPart.split(","); + int portSeparator = segments[0].lastIndexOf(":"); + Assert.isTrue(portSeparator != -1, "ClusterNode information does not define host and port"); + + String addressPart = getAddressPart(segments[0].substring(0, portSeparator)); + String portPart = getPortPart(segments[0].substring(portSeparator + 1)); + String hostnamePart = segments.length > 1 ? segments[1] : null; + return new AddressPortHostname(addressPart, portPart, hostnamePart); } - @NonNull private static String getAddressPart(String hostPort, int lastColon) { - // Everything to the left of port - // 127.0.0.1:6380 - // 127.0.0.1:6380@6381 - // :6380 - // :6380@6381 - // 2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380 - // 2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380@6381 - // 127.0.0.1:6380,hostname1 - // 127.0.0.1:6380@6381,hostname1 - // :6380,hostname1 - // :6380@6381,hostname1 - // 2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380,hostname1 - // 2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380@6381,hostname1 - String addressPart = hostPort.substring(0, lastColon); - // [2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380 - // [2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380@6381 - // [2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380,hostname1 - // [2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380@6381,hostname1 - if (addressPart.startsWith("[") && addressPart.endsWith("]")) { - addressPart = addressPart.substring(1, addressPart.length() - 1); + private static String getAddressPart(String address) { + return address.startsWith("[") && address.endsWith("]") ? address.substring(1, address.length() - 1) : address; + } + + private static String getPortPart(String segment) { + + if (segment.contains("@")) { + return segment.substring(0, segment.indexOf('@')); + } + + if (segment.contains(":")) { + return segment.substring(0, segment.indexOf(':')); } - return addressPart; + + return segment; + } + + public int portAsInt() { + return Integer.parseInt(port()); } - @Nullable - private static String getHostnamePart(boolean hasColon, String hostPort, int indexOfColon) { - // Everything to the right starting from comma - String hostnamePart = hasColon ? hostPort.substring(indexOfColon + 1) : null; - return StringUtils.hasText(hostnamePart) ? hostnamePart : null; + public boolean hasHostname() { + return StringUtils.hasText(hostname()); } - @NonNull private static String getPortPart(String hostPort, int lastColon, boolean hasColon, int indexOfColon) { - String portPart = hostPort.substring(lastColon + 1); - if (portPart.contains("@")) { - portPart = portPart.substring(0, portPart.indexOf("@")); - } else if (hasColon) { - portPart = portPart.substring(0, indexOfColon); + public String getRequiredHostname() { + + if (StringUtils.hasText(hostname())) { + return hostname(); } - return portPart; + + throw new IllegalStateException("Hostname not available"); } } @@ -637,24 +636,24 @@ public RedisClusterNode convert(String source) { String[] args = source.split(" "); - AddressPortHostname addressPortHostname = AddressPortHostname.of(args); - String addressPart = addressPortHostname.addressPart; - String portPart = addressPortHostname.portPart; - String hostnamePart = addressPortHostname.hostnamePart; + Assert.isTrue(args.length >= MASTER_ID_INDEX + 1, + () -> "Invalid ClusterNode information, insufficient segments: %s".formatted(source)); + + AddressPortHostname endpoint = AddressPortHostname.parse(args[HOST_PORT_INDEX]); SlotRange range = parseSlotRange(args); - Set flags = parseFlags(args); + Set flags = parseFlags(args[FLAGS_INDEX]); RedisClusterNodeBuilder nodeBuilder = RedisClusterNode.newRedisClusterNode() - .listeningAt(addressPart, Integer.parseInt(portPart)) // + .listeningAt(endpoint.address(), endpoint.portAsInt()) // .withId(args[ID_INDEX]) // .promotedAs(flags.contains(Flag.MASTER) ? NodeType.MASTER : NodeType.REPLICA) // .serving(range) // .withFlags(flags) // .linkState(parseLinkState(args)); - if (hostnamePart != null) { - nodeBuilder.withName(hostnamePart); + if (endpoint.hasHostname()) { + nodeBuilder.withName(endpoint.getRequiredHostname()); } if (!args[MASTER_ID_INDEX].isEmpty() && !args[MASTER_ID_INDEX].startsWith("-")) { @@ -664,14 +663,12 @@ public RedisClusterNode convert(String source) { return nodeBuilder.build(); } - private Set parseFlags(String[] args) { - - String raw = args[FLAGS_INDEX]; + private Set parseFlags(String source) { Set flags = new LinkedHashSet<>(8, 1); - if (StringUtils.hasText(raw)) { - for (String flag : raw.split(",")) { + if (StringUtils.hasText(source)) { + for (String flag : source.split(",")) { flags.add(flagLookupMap.get(flag)); } } diff --git a/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java b/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java index 7bd64f2135..6bc5938be3 100644 --- a/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java @@ -20,16 +20,16 @@ import java.util.Iterator; import java.util.stream.Stream; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; + import org.springframework.data.redis.connection.RedisClusterNode; import org.springframework.data.redis.connection.RedisClusterNode.Flag; import org.springframework.data.redis.connection.RedisClusterNode.LinkState; import org.springframework.data.redis.connection.RedisNode.NodeType; -import org.springframework.data.redis.connection.convert.Converters.ClusterNodesConverter; +import org.springframework.data.redis.connection.convert.Converters.ClusterNodesConverter.AddressPortHostname; /** * Unit tests for {@link Converters}. @@ -37,6 +37,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author Sorokin Evgeniy + * @author Marcin Grzejszczak */ class ConvertersUnitTests { @@ -252,37 +253,35 @@ void toClusterNodeWithIPv6Hostname() { assertThat(node.getSlotRange().getSlots().size()).isEqualTo(5461); } - @Test // https://github.com/spring-projects/spring-data-redis/issues/2862 + @Test // GH-2862 void toClusterNodeWithIPv4EmptyHostname() { + RedisClusterNode node = Converters.toClusterNode(CLUSTER_NODE_WITH_SINGLE_IPV4_EMPTY_HOSTNAME); - SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(node.getId()).isEqualTo("3765733728631672640db35fd2f04743c03119c6"); - softAssertions.assertThat(node.getHost()).isEqualTo("10.180.0.33"); - softAssertions.assertThat(node.hasValidHost()).isTrue(); - softAssertions.assertThat(node.getPort()).isEqualTo(11003); - softAssertions.assertThat(node.getType()).isEqualTo(NodeType.MASTER); - softAssertions.assertThat(node.getFlags()).contains(Flag.MASTER); - softAssertions.assertThat(node.getLinkState()).isEqualTo(LinkState.CONNECTED); - softAssertions.assertThat(node.getSlotRange().getSlots().size()).isEqualTo(5461); - }); + assertThat(node.getId()).isEqualTo("3765733728631672640db35fd2f04743c03119c6"); + assertThat(node.getHost()).isEqualTo("10.180.0.33"); + assertThat(node.hasValidHost()).isTrue(); + assertThat(node.getPort()).isEqualTo(11003); + assertThat(node.getType()).isEqualTo(NodeType.MASTER); + assertThat(node.getFlags()).contains(Flag.MASTER); + assertThat(node.getLinkState()).isEqualTo(LinkState.CONNECTED); + assertThat(node.getSlotRange().getSlots().size()).isEqualTo(5461); } - @Test // https://github.com/spring-projects/spring-data-redis/issues/2862 + @Test // GH-2862 void toClusterNodeWithIPv4Hostname() { + RedisClusterNode node = Converters.toClusterNode(CLUSTER_NODE_WITH_SINGLE_IPV4_HOSTNAME); - SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(node.getId()).isEqualTo("3765733728631672640db35fd2f04743c03119c6"); - softAssertions.assertThat(node.getHost()).isEqualTo("10.180.0.33"); - softAssertions.assertThat(node.getName()).isEqualTo("hostname1"); - softAssertions.assertThat(node.hasValidHost()).isTrue(); - softAssertions.assertThat(node.getPort()).isEqualTo(11003); - softAssertions.assertThat(node.getType()).isEqualTo(NodeType.MASTER); - softAssertions.assertThat(node.getFlags()).contains(Flag.MASTER); - softAssertions.assertThat(node.getLinkState()).isEqualTo(LinkState.CONNECTED); - softAssertions.assertThat(node.getSlotRange().getSlots().size()).isEqualTo(5461); - }); + assertThat(node.getId()).isEqualTo("3765733728631672640db35fd2f04743c03119c6"); + assertThat(node.getHost()).isEqualTo("10.180.0.33"); + assertThat(node.getName()).isEqualTo("hostname1"); + assertThat(node.hasValidHost()).isTrue(); + assertThat(node.getPort()).isEqualTo(11003); + assertThat(node.getType()).isEqualTo(NodeType.MASTER); + assertThat(node.getFlags()).contains(Flag.MASTER); + assertThat(node.getLinkState()).isEqualTo(LinkState.CONNECTED); + assertThat(node.getSlotRange().getSlots().size()).isEqualTo(5461); } @Test // GH-2678 @@ -308,34 +307,64 @@ void toClusterNodeWithInvalidIPv6Hostname() { @ParameterizedTest // GH-2678 @MethodSource("clusterNodesEndpoints") - void shouldAcceptHostPatterns(String endpoint, String expectedAddress, String expectedPort, String expectedHostname) { + void shouldAcceptHostPatterns(String endpoint, AddressPortHostname expected) { - ClusterNodesConverter.AddressPortHostname addressPortHostname = ClusterNodesConverter.AddressPortHostname.of(new String[] { "id", endpoint }); + AddressPortHostname addressPortHostname = AddressPortHostname.parse(endpoint); - assertThat(addressPortHostname.addressPart()).isEqualTo(expectedAddress); - assertThat(addressPortHostname.portPart()).isEqualTo(expectedPort); - assertThat(addressPortHostname.hostnamePart()).isEqualTo(expectedHostname); + assertThat(addressPortHostname).isEqualTo(expected); } static Stream clusterNodesEndpoints() { - return Stream.of( + Stream regular = Stream.of( // IPv4 with Host, Redis 3 - Arguments.of("1.2.4.4:7379", "1.2.4.4", "7379", null), + Arguments.of("1.2.4.4:7379", new AddressPortHostname("1.2.4.4", "7379", null)), // IPv6 with Host, Redis 3 - Arguments.of("6b8:c67:9c:0:6d8b:33da:5a2c:6380", "6b8:c67:9c:0:6d8b:33da:5a2c", "6380", null), + Arguments.of("6b8:c67:9c:0:6d8b:33da:5a2c:6380", + new AddressPortHostname("6b8:c67:9c:0:6d8b:33da:5a2c", "6380", null)), // Assuming IPv6 in brackets with Host, Redis 3 - Arguments.of("[6b8:c67:9c:0:6d8b:33da:5a2c]:6380", "6b8:c67:9c:0:6d8b:33da:5a2c", "6380", null), + Arguments.of("[6b8:c67:9c:0:6d8b:33da:5a2c]:6380", + new AddressPortHostname("6b8:c67:9c:0:6d8b:33da:5a2c", "6380", null)), // IPv4 with Host and Bus Port, Redis 4 - Arguments.of("127.0.0.1:7382@17382", "127.0.0.1", "7382", null), + Arguments.of("127.0.0.1:7382@17382", new AddressPortHostname("127.0.0.1", "7382", null)), // IPv6 with Host and Bus Port, Redis 4 - Arguments.of("6b8:c67:9c:0:6d8b:33da:5a2c:6380", "6b8:c67:9c:0:6d8b:33da:5a2c", "6380", null), + Arguments.of("6b8:c67:9c:0:6d8b:33da:5a2c:6380", + new AddressPortHostname("6b8:c67:9c:0:6d8b:33da:5a2c", "6380", null)), // Hostname with Port and Bus Port, Redis 7 - Arguments.of("my.host-name.com:7379@17379", "my.host-name.com", "7379", null), + Arguments.of("my.host-name.com:7379@17379", new AddressPortHostname("my.host-name.com", "7379", null)), // With hostname, Redis 7 - Arguments.of("1.2.4.4:7379@17379,my.host-name.com", "1.2.4.4", "7379", "my.host-name.com")); + Arguments.of("1.2.4.4:7379@17379,my.host-name.com", + new AddressPortHostname("1.2.4.4", "7379", "my.host-name.com"))); + + Stream weird = Stream.of( + // Port-only + Arguments.of(":6380", new AddressPortHostname("", "6380", null)), + + // Port-only with bus-port + Arguments.of(":6380@6381", new AddressPortHostname("", "6380", null)), + // IP with trailing comma + Arguments.of("127.0.0.1:6380,", new AddressPortHostname("127.0.0.1", "6380", null)), + // IPv6 with bus-port + Arguments.of("2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380@6381", + new AddressPortHostname("2a02:6b8:c67:9c:0:6d8b:33da:5a2c", "6380", null)), + // IPv6 with bus-port and hostname + Arguments.of("2a02:6b8:c67:9c:0:6d8b:33da:5a2c:6380@6381,hostname1", + new AddressPortHostname("2a02:6b8:c67:9c:0:6d8b:33da:5a2c", "6380", "hostname1")), + // Port-only with hostname + Arguments.of(":6380,hostname1", new AddressPortHostname("", "6380", "hostname1")), + + // Port-only with bus-port + Arguments.of(":6380@6381,hostname1", new AddressPortHostname("", "6380", "hostname1")), + // IPv6 in brackets with bus-port + Arguments.of("[2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380@6381", + new AddressPortHostname("2a02:6b8:c67:9c:0:6d8b:33da:5a2c", "6380", null)), + // IPv6 in brackets with bus-port and hostname + Arguments.of("[2a02:6b8:c67:9c:0:6d8b:33da:5a2c]:6380@6381,hostname1", + new AddressPortHostname("2a02:6b8:c67:9c:0:6d8b:33da:5a2c", "6380", "hostname1"))); + + return Stream.concat(regular, weird); } } From 8c74d78872f11f52488562afc3773fcb0e1d1b8b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 16 Sep 2024 14:35:10 +0200 Subject: [PATCH 074/187] Support deserialization in `GenericJackson2JsonRedisSerializer` when using custom `JsonFactory`. Closes: #2981 Original Pull Request: #2999 --- pom.xml | 7 +++ .../GenericJackson2JsonRedisSerializer.java | 53 ++++++++++++++++--- ...cJackson2JsonRedisSerializerUnitTests.java | 27 ++++++++++ 3 files changed, 81 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 09bebeb5bd..2252974424 100644 --- a/pom.xml +++ b/pom.xml @@ -276,6 +276,13 @@ test + + org.msgpack + jackson-dataformat-msgpack + 0.9.8 + test + + edu.umd.cs.mtc multithreadedtc diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index 7ecc89d18c..9358f36a7e 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -32,13 +32,19 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.BeanDeserializerFactory; +import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext; +import com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer; import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; @@ -179,7 +185,7 @@ private static TypeResolver newTypeResolver(ObjectMapper mapper, @Nullable Strin Lazy lazyTypeHintPropertyName = typeHintPropertyName != null ? Lazy.of(typeHintPropertyName) : newLazyTypeHintPropertyName(mapper, defaultTypingEnabled); - return new TypeResolver(lazyTypeFactory, lazyTypeHintPropertyName); + return new TypeResolver(mapper, lazyTypeFactory, lazyTypeHintPropertyName); } private static Lazy newLazyTypeHintPropertyName(ObjectMapper mapper, Lazy defaultTypingEnabled) { @@ -340,14 +346,13 @@ protected JavaType resolveType(byte[] source, Class type) throws IOException */ static class TypeResolver { - // need a separate instance to bypass class hint checks - private final ObjectMapper mapper = new ObjectMapper(); - + private final ObjectMapper mapper; private final Supplier typeFactory; private final Supplier hintName; - TypeResolver(Supplier typeFactory, Supplier hintName) { + TypeResolver(ObjectMapper mapper, Supplier typeFactory, Supplier hintName) { + this.mapper = mapper; this.typeFactory = typeFactory; this.hintName = hintName; } @@ -358,7 +363,7 @@ protected JavaType constructType(Class type) { protected JavaType resolveType(byte[] source, Class type) throws IOException { - JsonNode root = mapper.readTree(source); + JsonNode root = readTree(source); JsonNode jsonNode = root.get(hintName.get()); if (jsonNode instanceof TextNode && jsonNode.asText() != null) { @@ -367,6 +372,42 @@ protected JavaType resolveType(byte[] source, Class type) throws IOException return constructType(type); } + + /** + * Lenient variant of ObjectMapper._readTreeAndClose using a strict {@link JsonNodeDeserializer}. + * + * @param source + * @return + * @throws IOException + */ + private JsonNode readTree(byte[] source) throws IOException { + + JsonDeserializer deserializer = JsonNodeDeserializer.getDeserializer(JsonNode.class); + DeserializationConfig cfg = mapper.getDeserializationConfig(); + + try (JsonParser parser = mapper.createParser(source)) { + + cfg.initialize(parser); + JsonToken t = parser.currentToken(); + if (t == null) { + t = parser.nextToken(); + if (t == null) { + return cfg.getNodeFactory().missingNode(); + } + } + + /* + * Hokey pokey! Oh my. + */ + DefaultDeserializationContext ctxt = new DefaultDeserializationContext.Impl(BeanDeserializerFactory.instance) + .createInstance(cfg, parser, mapper.getInjectableValues()); + if (t == JsonToken.VALUE_NULL) { + return cfg.getNodeFactory().nullNode(); + } else { + return deserializer.deserialize(parser, ctxt); + } + } + } } /** diff --git a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java index 16daa677c8..c91e6b46f7 100644 --- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.msgpack.jackson.dataformat.MessagePackFactory; import org.springframework.beans.BeanUtils; import org.springframework.cache.support.NullValue; @@ -449,6 +450,7 @@ void configureWithNullConsumerThrowsIllegalArgumentException() { @Test void defaultSerializeAndDeserializeNullValueWithBuilderClass() { + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() .objectMapper(new ObjectMapper().enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY)) .build(); @@ -487,6 +489,31 @@ public void serializeWithType(NullValue value, JsonGenerator jsonGenerator, Seri assertThat(deserializedValue).isNull(); } + @Test // GH-2981 + void defaultSerializeAndDeserializeWithCustomJsonFactory() { + + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() + .objectMapper( + new ObjectMapper(new MessagePackFactory()).enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY)) + .build(); + + byte[] serializedValue = serializer.serialize(COMPLEX_OBJECT); + + Object deserializedValue = serializer.deserialize(serializedValue, Object.class); + assertThat(deserializedValue).isEqualTo(COMPLEX_OBJECT); + } + + @Test // GH-2981 + void defaultSerializeAndDeserializeNullValueWithBuilderClassAndCustomJsonFactory() { + + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() + .objectMapper( + new ObjectMapper(new MessagePackFactory()).enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY)) + .build(); + + serializeAndDeserializeNullValue(serializer); + } + private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) { NullValue nv = BeanUtils.instantiateClass(NullValue.class); From f82b399150b858343d98e42b81875d23af36aeca Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 9 Oct 2024 11:52:59 +0200 Subject: [PATCH 075/187] Polishing. Move JsonParser creation to dedicated method. Original Pull Request: #2999 --- .../serializer/GenericJackson2JsonRedisSerializer.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index 9358f36a7e..f989e27802 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -385,9 +385,8 @@ private JsonNode readTree(byte[] source) throws IOException { JsonDeserializer deserializer = JsonNodeDeserializer.getDeserializer(JsonNode.class); DeserializationConfig cfg = mapper.getDeserializationConfig(); - try (JsonParser parser = mapper.createParser(source)) { + try (JsonParser parser = createParser(source, cfg)) { - cfg.initialize(parser); JsonToken t = parser.currentToken(); if (t == null) { t = parser.nextToken(); @@ -408,6 +407,13 @@ private JsonNode readTree(byte[] source) throws IOException { } } } + + private JsonParser createParser(byte[] source, DeserializationConfig cfg) throws IOException { + + JsonParser parser = mapper.createParser(source); + cfg.initialize(parser); + return parser; + } } /** From 6d1e635470e1bba89498bba627a1bde8034efcd3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 9 Oct 2024 14:17:13 +0200 Subject: [PATCH 076/187] Use Docker in Docker CI setup. See #2994 --- Jenkinsfile | 14 ++++++++------ ci/test.sh | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index b10529fb9f..0344ab7349 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -122,7 +122,7 @@ pipeline { steps { script { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { - docker.image("springci/spring-data-with-redis-6.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + docker.image("springci/spring-data-with-redis-6.2:${p['java.main.tag']}").inside(p['docker.java.inside.docker']) { sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" } } @@ -151,7 +151,7 @@ pipeline { steps { script { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { - docker.image("springci/spring-data-with-redis-6.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + docker.image("springci/spring-data-with-redis-6.2:${p['java.main.tag']}").inside(p['docker.java.inside.docker']) { sh "PROFILE=runtimehints LONG_TESTS=false JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" } } @@ -170,7 +170,7 @@ pipeline { steps { script { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { - docker.image("springci/spring-data-with-redis-6.2:${p['java.next.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + docker.image("springci/spring-data-with-redis-6.2:${p['java.next.tag']}").inside(p['docker.java.inside.docker']) { sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" } } @@ -189,7 +189,7 @@ pipeline { steps { script { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { - docker.image("springci/spring-data-with-redis-7.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + docker.image("springci/spring-data-with-redis-7.2:${p['java.main.tag']}").inside(p['docker.java.inside.docker']) { sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" } } @@ -209,7 +209,7 @@ pipeline { steps { script { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { - docker.image("springci/spring-data-with-valkey-7.2:${p['java.main.tag']}").inside('-v $HOME:/tmp/jenkins-home') { + docker.image("springci/spring-data-with-valkey-7.2:${p['java.main.tag']}").inside(p['docker.java.inside.docker']) { sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" } } @@ -240,15 +240,17 @@ pipeline { steps { script { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { - docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { + docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' + "./mvnw -s settings.xml -Pci,artifactory " + + "-Ddevelocity.storage.directory=/tmp/jenkins-home/.develocity-root " + "-Dartifactory.server=${p['artifactory.url']} " + "-Dartifactory.username=${ARTIFACTORY_USR} " + "-Dartifactory.password=${ARTIFACTORY_PSW} " + "-Dartifactory.staging-repository=${p['artifactory.repository.snapshot']} " + "-Dartifactory.build-name=spring-data-redis " + "-Dartifactory.build-number=spring-data-redis-${BRANCH_NAME}-build-${BUILD_NUMBER} " + + "-Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-redis " + "-Dmaven.test.skip=true clean deploy -U -B" } } diff --git a/ci/test.sh b/ci/test.sh index 44267f8b7a..5bee10b054 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -13,7 +13,7 @@ pushd /tmp && ln -s /work && make -f $cwd/Makefile start && popd export JENKINS_USER=${JENKINS_USER_NAME} # Execute maven test -MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean test -P${PROFILE} -DrunLongTests=${LONG_TESTS:-false} -Dredis.server.version=${REDIS_VERSION:-unknown} -U -B +MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Ddevelocity.storage.directory=/tmp/jenkins-home/.develocity-root -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-redis clean test -P${PROFILE} -DrunLongTests=${LONG_TESTS:-false} -Dredis.server.version=${REDIS_VERSION:-unknown} -U -B # Capture resulting exit code from maven (pass/fail) RESULT=$? From cf42be1c421abacbb0283e60e1da7359851f00a8 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 10 Oct 2024 09:04:32 +0200 Subject: [PATCH 077/187] Upgrade to Jedis 5.2.0. Closes #3008 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2252974424..b20be3b3a5 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 1.4.20 2.11.1 6.4.0.RELEASE - 5.1.5 + 5.2.0 1.01 4.1.107.Final spring.data.redis From feb534f3e8dbf55aa31b8cc55ce2f56ff99bbbee Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 10 Oct 2024 09:28:49 +0200 Subject: [PATCH 078/187] Introduce `JedisClientConfigBuilderCustomizer` to support client config customization. We now support customization of Jedis' JedisClientConfig that is used for various client configurations for setting extended properties that Spring Data Redis doesn't configure itself. Closes: #3007 Original Pull Request: #3014 --- .../DefaultJedisClientConfiguration.java | 10 ++++- .../JedisClientConfigBuilderCustomizer.java | 39 +++++++++++++++++++ .../jedis/JedisClientConfiguration.java | 31 ++++++++++++++- .../jedis/JedisConnectionFactory.java | 7 ++++ .../JedisConnectionFactoryUnitTests.java | 26 +++++++++++++ 5 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java index 50fd68211e..683c0ec55a 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java @@ -34,6 +34,7 @@ */ class DefaultJedisClientConfiguration implements JedisClientConfiguration { + private final Optional customizer; private final boolean useSsl; private final Optional sslSocketFactory; private final Optional sslParameters; @@ -44,11 +45,13 @@ class DefaultJedisClientConfiguration implements JedisClientConfiguration { private final Duration readTimeout; private final Duration connectTimeout; - DefaultJedisClientConfiguration(boolean useSsl, @Nullable SSLSocketFactory sslSocketFactory, + DefaultJedisClientConfiguration(@Nullable JedisClientConfigBuilderCustomizer customizer, boolean useSsl, + @Nullable SSLSocketFactory sslSocketFactory, @Nullable SSLParameters sslParameters, @Nullable HostnameVerifier hostnameVerifier, boolean usePooling, @Nullable GenericObjectPoolConfig poolConfig, @Nullable String clientName, Duration readTimeout, Duration connectTimeout) { + this.customizer = Optional.ofNullable(customizer); this.useSsl = useSsl; this.sslSocketFactory = Optional.ofNullable(sslSocketFactory); this.sslParameters = Optional.ofNullable(sslParameters); @@ -60,6 +63,11 @@ class DefaultJedisClientConfiguration implements JedisClientConfiguration { this.connectTimeout = connectTimeout; } + @Override + public Optional getCustomizer() { + return customizer; + } + @Override public boolean isUseSsl() { return useSsl; diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java new file mode 100644 index 0000000000..8f508c374f --- /dev/null +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.connection.jedis; + +import redis.clients.jedis.DefaultJedisClientConfig; + +/** + * Strategy interface for customizing {@link DefaultJedisClientConfig.Builder JedisClientConfig}. Any ClientConfig will + * be used to call this interface implementation so you can set the protocol, client name, etc. after Spring has applies + * its defaults. + * + * @author Mark Paluch + * @since 3.4 + * @see redis.clients.jedis.DefaultJedisClientConfig.Builder + */ +@FunctionalInterface +public interface JedisClientConfigBuilderCustomizer { + + /** + * Customize the {@link DefaultJedisClientConfig.Builder}. + * + * @param builder the builder to customize. + */ + void customize(DefaultJedisClientConfig.Builder builder); + +} diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java index 1c9628db0c..b6b9a22467 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java @@ -58,6 +58,12 @@ */ public interface JedisClientConfiguration { + /** + * @return the optional {@link JedisClientConfigBuilderCustomizer}. + * @since 3.4 + */ + Optional getCustomizer(); + /** * @return {@literal true} to use SSL, {@literal false} to use unencrypted connections. */ @@ -119,6 +125,8 @@ static JedisClientConfigurationBuilder builder() { /** * Creates a default {@link JedisClientConfiguration}. *
    + *
    Customizer
    + *
    none
    *
    SSL enabled
    *
    no
    *
    Pooling enabled
    @@ -142,6 +150,15 @@ static JedisClientConfiguration defaultConfiguration() { */ interface JedisClientConfigurationBuilder { + /** + * Configure a {@link JedisClientConfigBuilderCustomizer} to configure + * {@link redis.clients.jedis.JedisClientConfig}. + * + * @return {@link JedisClientConfigurationBuilder}. + * @since 3.4 + */ + JedisClientConfigurationBuilder customize(JedisClientConfigBuilderCustomizer customizer); + /** * Enable SSL connections. * @@ -269,6 +286,7 @@ interface JedisSslClientConfigurationBuilder { class DefaultJedisClientConfigurationBuilder implements JedisClientConfigurationBuilder, JedisPoolingClientConfigurationBuilder, JedisSslClientConfigurationBuilder { + private @Nullable JedisClientConfigBuilderCustomizer customizer; private boolean useSsl; private @Nullable SSLSocketFactory sslSocketFactory; private @Nullable SSLParameters sslParameters; @@ -281,6 +299,15 @@ class DefaultJedisClientConfigurationBuilder implements JedisClientConfiguration private DefaultJedisClientConfigurationBuilder() {} + @Override + public JedisClientConfigurationBuilder customize(JedisClientConfigBuilderCustomizer customizer) { + + Assert.notNull(customizer, "JedisClientConfigBuilderCustomizer must not be null"); + + this.customizer = customizer; + return this; + } + @Override public JedisSslClientConfigurationBuilder useSsl() { @@ -366,8 +393,8 @@ public JedisClientConfigurationBuilder connectTimeout(Duration connectTimeout) { @Override public JedisClientConfiguration build() { - return new DefaultJedisClientConfiguration(useSsl, sslSocketFactory, sslParameters, hostnameVerifier, usePooling, - poolConfig, clientName, readTimeout, connectTimeout); + return new DefaultJedisClientConfiguration(customizer, useSsl, sslSocketFactory, sslParameters, hostnameVerifier, + usePooling, poolConfig, clientName, readTimeout, connectTimeout); } } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java index 21bd50df5e..c2ecc3e2cf 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java @@ -711,6 +711,8 @@ private JedisClientConfig createClientConfig(int database, @Nullable String user this.clientConfiguration.getSslParameters().ifPresent(builder::sslParameters); } + this.clientConfiguration.getCustomizer().ifPresent(customizer -> customizer.customize(builder)); + return builder.build(); } @@ -1087,6 +1089,11 @@ public static JedisClientConfiguration create(GenericObjectPoolConfig jedisPoolC return configuration; } + @Override + public Optional getCustomizer() { + return Optional.empty(); + } + @Override public boolean isUseSsl() { return useSsl; diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java index f6e3f3f9e4..b553cf4415 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java @@ -18,9 +18,11 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.RedisProtocol; import java.io.IOException; import java.security.NoSuchAlgorithmException; @@ -338,6 +340,30 @@ void afterPropertiesTriggersConnectionInitialization() { assertThat(connectionFactory.isRunning()).isTrue(); } + @Test // GH-3007 + void clientConfigurationAppliesCustomizer() { + + JedisClientConfig resp3Config = apply( + JedisClientConfiguration.builder().customize(DefaultJedisClientConfig.Builder::resp3).build()); + + assertThat(resp3Config.getRedisProtocol()).isEqualTo(RedisProtocol.RESP3); + + JedisClientConfig resp2Config = apply( + JedisClientConfiguration.builder().customize(it -> it.protocol(RedisProtocol.RESP2)).build()); + + assertThat(resp2Config.getRedisProtocol()).isEqualTo(RedisProtocol.RESP2); + } + + private static JedisClientConfig apply(JedisClientConfiguration configuration) { + + JedisConnectionFactory connectionFactory = new JedisConnectionFactory(new RedisStandaloneConfiguration(), + configuration); + connectionFactory.setEarlyStartup(false); + connectionFactory.afterPropertiesSet(); + + return (JedisClientConfig) ReflectionTestUtils.getField(connectionFactory, "clientConfig"); + } + @Test // GH-2866 void earlyStartupDoesNotStartConnectionFactory() { From 7bedd602e72fdc543f82ffc97f9703214015d582 Mon Sep 17 00:00:00 2001 From: jinia91 Date: Wed, 25 Sep 2024 11:27:55 +0900 Subject: [PATCH 079/187] Update `RedisMessageListenerContainer` docs. Closes #3005 --- .../data/redis/listener/RedisMessageListenerContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java index 63b111528b..8008f8d7c3 100644 --- a/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java @@ -117,7 +117,7 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab public static final long DEFAULT_SUBSCRIPTION_REGISTRATION_WAIT_TIME = 2000L; /** - * Default thread name prefix: "RedisListeningContainer-". + * Default thread name prefix: "RedisMessageListenerContainer-". */ public static final String DEFAULT_THREAD_NAME_PREFIX = ClassUtils.getShortName(RedisMessageListenerContainer.class) + "-"; From 31b6736c6b8ea4cd98d5b0017d7472f1c3fb1c1f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 10 Oct 2024 14:30:05 +0200 Subject: [PATCH 080/187] Consistently accept varargs with Redis Scripting. Closes #3010 --- .../modules/ROOT/pages/redis/scripting.adoc | 4 +-- .../redis/core/ReactiveRedisOperations.java | 14 ++++++++++ .../core/script/ReactiveScriptExecutor.java | 27 ++++++++++++++----- .../redis/core/script/ScriptExecutor.java | 24 ++++++++--------- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/main/antora/modules/ROOT/pages/redis/scripting.adoc b/src/main/antora/modules/ROOT/pages/redis/scripting.adoc index 436c50d11c..42411fbd9c 100644 --- a/src/main/antora/modules/ROOT/pages/redis/scripting.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/scripting.adoc @@ -34,7 +34,7 @@ public class Example { RedisScript script; public boolean checkAndSet(String expectedValue, String newValue) { - return redisOperations.execute(script, singletonList("key"), asList(expectedValue, newValue)); + return redisOperations.execute(script, List.of("key"), expectedValue, newValue); } } ---- @@ -52,7 +52,7 @@ public class Example { RedisScript script; public Flux checkAndSet(String expectedValue, String newValue) { - return redisOperations.execute(script, singletonList("key"), asList(expectedValue, newValue)); + return redisOperations.execute(script, List.of("key"), expectedValue, newValue); } } ---- diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java index c02d1eea81..4f123c9cbc 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java @@ -429,6 +429,20 @@ default Flux execute(RedisScript script, List keys) { return execute(script, keys, Collections.emptyList()); } + /** + * Executes the given {@link RedisScript} + * + * @param script The script to execute. Must not be {@literal null}. + * @param keys keys that need to be passed to the script. Must not be {@literal null}. + * @param args args that need to be passed to the script. Must not be {@literal null}. + * @return result value of the script {@link Flux#empty()} if {@link RedisScript#getResultType()} is {@literal null}, + * likely indicating a throw-away status reply (i.e. "OK"). + * @since 3.4 + */ + default Flux execute(RedisScript script, List keys, Object... args) { + return execute(script, keys, Arrays.asList(args)); + } + /** * Executes the given {@link RedisScript} * diff --git a/src/main/java/org/springframework/data/redis/core/script/ReactiveScriptExecutor.java b/src/main/java/org/springframework/data/redis/core/script/ReactiveScriptExecutor.java index b9b765d96f..6cd520f394 100644 --- a/src/main/java/org/springframework/data/redis/core/script/ReactiveScriptExecutor.java +++ b/src/main/java/org/springframework/data/redis/core/script/ReactiveScriptExecutor.java @@ -18,6 +18,7 @@ import reactor.core.publisher.Flux; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -30,8 +31,8 @@ *

    * Streams of methods returning {@code Mono} or {@code Flux} are terminated with * {@link org.springframework.dao.InvalidDataAccessApiUsageException} when - * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@code null} for a - * particular element as Reactive Streams prohibit the usage of {@code null} values. + * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@literal null} for a + * particular element as Reactive Streams prohibit the usage of {@literal null} values. * * @author Mark Paluch * @author Christoph Strobl @@ -67,8 +68,22 @@ default Flux execute(RedisScript script, List keys) { * Executes the given {@link RedisScript} * * @param script The script to execute. Must not be {@literal null}. - * @param keys Any keys that need to be passed to the script. Must not be {@literal null}. - * @param args Any args that need to be passed to the script. Can be {@literal empty}. + * @param keys any keys that need to be passed to the script. Must not be {@literal null}. + * @param args any args that need to be passed to the script. Can be {@literal empty}. + * @return The return value of the script or {@link Flux#empty()} if {@link RedisScript#getResultType()} is + * {@literal null}, likely indicating a throw-away status reply (i.e. "OK") + * @since 3.4 + */ + default Flux execute(RedisScript script, List keys, Object... args) { + return execute(script, keys, Arrays.asList(args)); + } + + /** + * Executes the given {@link RedisScript} + * + * @param script The script to execute. Must not be {@literal null}. + * @param keys any keys that need to be passed to the script. Must not be {@literal null}. + * @param args any args that need to be passed to the script. Can be {@literal empty}. * @return The return value of the script or {@link Flux#empty()} if {@link RedisScript#getResultType()} is * {@literal null}, likely indicating a throw-away status reply (i.e. "OK") */ @@ -79,8 +94,8 @@ default Flux execute(RedisScript script, List keys) { * arguments and result. * * @param script The script to execute. must not be {@literal null}. - * @param keys Any keys that need to be passed to the script - * @param args Any args that need to be passed to the script + * @param keys any keys that need to be passed to the script. + * @param args any args that need to be passed to the script. * @param argsWriter The {@link RedisElementWriter} to use for serializing args. Must not be {@literal null}. * @param resultReader The {@link RedisElementReader} to use for serializing the script return value. Must not be * {@literal null}. diff --git a/src/main/java/org/springframework/data/redis/core/script/ScriptExecutor.java b/src/main/java/org/springframework/data/redis/core/script/ScriptExecutor.java index b7b658a7e4..953f27fbb1 100644 --- a/src/main/java/org/springframework/data/redis/core/script/ScriptExecutor.java +++ b/src/main/java/org/springframework/data/redis/core/script/ScriptExecutor.java @@ -30,11 +30,11 @@ public interface ScriptExecutor { /** * Executes the given {@link RedisScript} * - * @param script The script to execute - * @param keys Any keys that need to be passed to the script - * @param args Any args that need to be passed to the script - * @return The return value of the script or null if {@link RedisScript#getResultType()} is null, likely indicating a - * throw-away status reply (i.e. "OK") + * @param script the script to execute. + * @param keys any keys that need to be passed to the script. + * @param args any args that need to be passed to the script. + * @return The return value of the script or {@literal null} if {@link RedisScript#getResultType()} is + * {@literal null}, likely indicating a throw-away status reply (i.e. "OK") */ T execute(RedisScript script, List keys, Object... args); @@ -42,13 +42,13 @@ public interface ScriptExecutor { * Executes the given {@link RedisScript}, using the provided {@link RedisSerializer}s to serialize the script * arguments and result. * - * @param script The script to execute - * @param argsSerializer The {@link RedisSerializer} to use for serializing args - * @param resultSerializer The {@link RedisSerializer} to use for serializing the script return value - * @param keys Any keys that need to be passed to the script - * @param args Any args that need to be passed to the script - * @return The return value of the script or null if {@link RedisScript#getResultType()} is null, likely indicating a - * throw-away status reply (i.e. "OK") + * @param script the script to execute. + * @param argsSerializer The {@link RedisSerializer} to use for serializing args. + * @param resultSerializer The {@link RedisSerializer} to use for serializing the script return value. + * @param keys any keys that need to be passed to the script. + * @param args any args that need to be passed to the script. + * @return The return value of the script or {@literal null} if {@link RedisScript#getResultType()} is + * {@literal null}, likely indicating a throw-away status reply (i.e. "OK") */ T execute(RedisScript script, RedisSerializer argsSerializer, RedisSerializer resultSerializer, List keys, Object... args); From 6e85141310138bcf99843bc93abcbaa1236afb82 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 10 Oct 2024 14:36:08 +0200 Subject: [PATCH 081/187] Polishing. Consistently use literal null. See #3010 --- .../data/redis/cache/DefaultRedisCacheWriter.java | 2 +- .../data/redis/cache/RedisCacheWriter.java | 2 +- .../data/redis/connection/ReactiveHashCommands.java | 4 ++-- .../data/redis/connection/ReactiveStringCommands.java | 4 ++-- .../data/redis/connection/RedisHashCommands.java | 2 +- .../data/redis/connection/RedisStringCommands.java | 4 ++-- .../data/redis/connection/lettuce/LettuceFutureUtils.java | 4 ++-- .../data/redis/core/BoundHashOperations.java | 2 +- .../springframework/data/redis/core/HashOperations.java | 2 +- .../data/redis/core/ReactiveHashOperations.java | 6 +++--- .../data/redis/core/ReactiveListOperations.java | 4 ++-- .../data/redis/core/ReactiveRedisOperations.java | 4 ++-- .../data/redis/core/ReactiveRedisTemplate.java | 4 ++-- .../data/redis/core/ReactiveSetOperations.java | 4 ++-- .../data/redis/core/ReactiveValueOperations.java | 6 +++--- .../data/redis/core/ReactiveZSetOperations.java | 4 ++-- .../springframework/data/redis/core/RedisCallback.java | 8 ++++---- .../data/redis/core/RedisConnectionUtils.java | 2 +- .../org/springframework/data/redis/core/ScanCursor.java | 8 ++++---- .../springframework/data/redis/core/ValueOperations.java | 2 +- .../springframework/data/redis/core/convert/Bucket.java | 4 ++-- .../data/redis/test/condition/RedisConditions.java | 6 +++--- 22 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java index f5a73e6ea6..d8e1b831e7 100644 --- a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java @@ -481,7 +481,7 @@ interface AsyncCacheWriter { * @param name the cache name from which to retrieve the cache entry. * @param key the cache entry key. * @param ttl optional TTL to set for Time-to-Idle eviction. - * @return a future that completes either with a value if the value exists or completing with {@code null} if the + * @return a future that completes either with a value if the value exists or completing with {@literal null} if the * cache does not contain an entry. */ CompletableFuture retrieve(String name, byte[] key, @Nullable Duration ttl); diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java index 2da307ed8b..61e7f49d1d 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java @@ -319,7 +319,7 @@ static TtlFunction persistent() { * persistent value that does not expire. * * @param key the cache key. - * @param value the cache value. Can be {@code null} if the cache supports {@code null} value caching. + * @param value the cache value. Can be {@literal null} if the cache supports {@literal null} value caching. * @return the computed {@link Duration time-to-live (TTL)}. Can be {@link Duration#ZERO} for persistent values * (i.e. cache entry does not expire). */ diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java index 063dd6e285..d75c2242a0 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java @@ -289,7 +289,7 @@ default Mono hGet(ByteBuffer key, ByteBuffer field) { /** * Get values for given {@literal fields} from hash at {@literal key}. Values are in the order of the requested keys. - * Absent field values are represented using {@code null} in the resulting {@link List}. + * Absent field values are represented using {@literal null} in the resulting {@link List}. * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. @@ -306,7 +306,7 @@ default Mono> hMGet(ByteBuffer key, Collection fiel /** * Get values for given {@literal fields} from hash at {@literal key}. Values are in the order of the requested keys. - * Absent field values are represented using {@code null} in the resulting {@link List}. + * Absent field values are represented using {@literal null} in the resulting {@link List}. * * @param commands must not be {@literal null}. * @return diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java index 493b055dae..f6d18dbcfb 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java @@ -350,7 +350,7 @@ default Mono getSet(ByteBuffer key, ByteBuffer value) { /** * Get multiple values in one batch. Values are in the order of the requested keys. Absent field values are - * represented using {@code null} in the resulting {@link List}. + * represented using {@literal null} in the resulting {@link List}. * * @param keys must not be {@literal null}. * @return @@ -365,7 +365,7 @@ default Mono> mGet(List keys) { /** * Get multiple values at for {@literal keysets} in batches. Values are in the order of the requested keys. Absent - * field values are represented using {@code null} in the resulting {@link List}. + * field values are represented using {@literal null} in the resulting {@link List}. * * @param keysets must not be {@literal null}. * @return diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java index 8642fc837e..d1587a7918 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java @@ -69,7 +69,7 @@ public interface RedisHashCommands { /** * Get values for given {@code fields} from hash at {@code key}. Values are in the order of the requested keys Absent - * field values are represented using {@code null} in the resulting {@link List}. + * field values are represented using {@literal null} in the resulting {@link List}. * * @param key must not be {@literal null}. * @param fields must not be {@literal empty}. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java index beac673984..fe2abfce88 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java @@ -86,10 +86,10 @@ enum BitOperation { /** * Get multiple {@code keys}. Values are in the order of the requested keys Absent field values are represented using - * {@code null} in the resulting {@link List}. + * {@literal null} in the resulting {@link List}. * * @param keys must not be {@literal null}. - * @return {@code null} when used in pipeline / transaction. + * @return {@literal null} when used in pipeline / transaction. * @see Redis Documentation: MGET */ @Nullable diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceFutureUtils.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceFutureUtils.java index f1f56e1b5c..764f6e7ff9 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceFutureUtils.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceFutureUtils.java @@ -87,8 +87,8 @@ static T join(CompletionStage future) throws RuntimeException, Completion /** * Returns a {@link Function} that ignores {@link CompletionStage#exceptionally(Function) exceptional completion} by - * recovering to {@code null}. This allows to progress with a previously failed {@link CompletionStage} without regard - * to the actual success/exception state. + * recovering to {@literal null}. This allows to progress with a previously failed {@link CompletionStage} without + * regard to the actual success/exception state. * * @return */ diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java index b7c30dc1b7..90e77b9ae5 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java @@ -61,7 +61,7 @@ public interface BoundHashOperations extends BoundKeyOperations { /** * Get values for given {@code keys} from the hash at the bound key. Values are in the order of the requested keys - * Absent field values are represented using {@code null} in the resulting {@link List}. + * Absent field values are represented using {@literal null} in the resulting {@link List}. * * @param keys must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. diff --git a/src/main/java/org/springframework/data/redis/core/HashOperations.java b/src/main/java/org/springframework/data/redis/core/HashOperations.java index 8e98e49f97..db97cdb10a 100644 --- a/src/main/java/org/springframework/data/redis/core/HashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/HashOperations.java @@ -61,7 +61,7 @@ public interface HashOperations { /** * Get values for given {@code hashKeys} from hash at {@code key}. Values are in the order of the requested keys - * Absent field values are represented using {@code null} in the resulting {@link List}. + * Absent field values are represented using {@literal null} in the resulting {@link List}. * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java index 0f2950654a..8e611e3594 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java @@ -28,8 +28,8 @@ *

    * Streams of methods returning {@code Mono} or {@code Flux} are terminated with * {@link org.springframework.dao.InvalidDataAccessApiUsageException} when - * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@code null} for a - * particular element as Reactive Streams prohibit the usage of {@code null} values. + * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@literal null} for a + * particular element as Reactive Streams prohibit the usage of {@literal null} values. * * @author Mark Paluch * @author Christoph Strobl @@ -66,7 +66,7 @@ public interface ReactiveHashOperations { /** * Get values for given {@code hashKeys} from hash at {@code key}. Values are in the order of the requested keys. - * Absent field values are represented using {@code null} in the resulting {@link List}. + * Absent field values are represented using {@literal null} in the resulting {@link List}. * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java index acaf2e4344..9862fedba6 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java @@ -34,8 +34,8 @@ *

    * Streams of methods returning {@code Mono} or {@code Flux} are terminated with * {@link org.springframework.dao.InvalidDataAccessApiUsageException} when - * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@code null} for a - * particular element as Reactive Streams prohibit the usage of {@code null} values. + * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@literal null} for a + * particular element as Reactive Streams prohibit the usage of {@literal null} values. * * @author Mark Paluch * @author Christoph Strobl diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java index 4f123c9cbc..052cc6d180 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java @@ -45,8 +45,8 @@ *

    * Streams of methods returning {@code Mono} or {@code Flux} are terminated with * {@link org.springframework.dao.InvalidDataAccessApiUsageException} when - * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@code null} for a - * particular element as Reactive Streams prohibit the usage of {@code null} values. + * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@literal null} for a + * particular element as Reactive Streams prohibit the usage of {@literal null} values. * * @author Mark Paluch * @author Christoph Strobl diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java index 7816a744a8..6e52ef0e7f 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java @@ -58,8 +58,8 @@ *

    * Streams of methods returning {@code Mono} or {@code Flux} are terminated with * {@link org.springframework.dao.InvalidDataAccessApiUsageException} when - * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@code null} for a - * particular element as Reactive Streams prohibit the usage of {@code null} values. + * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@literal null} for a + * particular element as Reactive Streams prohibit the usage of {@literal null} values. * * @author Mark Paluch * @author Christoph Strobl diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveSetOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveSetOperations.java index eb9e55e431..f9d8ce7842 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveSetOperations.java @@ -27,8 +27,8 @@ *

    * Streams of methods returning {@code Mono} or {@code Flux} are terminated with * {@link org.springframework.dao.InvalidDataAccessApiUsageException} when - * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@code null} for a - * particular element as Reactive Streams prohibit the usage of {@code null} values. + * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@literal null} for a + * particular element as Reactive Streams prohibit the usage of {@literal null} values. * * @author Mark Paluch * @author Christoph Strobl diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java index 2a0f44404c..d1ded81eae 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java @@ -30,8 +30,8 @@ *

    * Streams of methods returning {@code Mono} or {@code Flux} are terminated with * {@link org.springframework.dao.InvalidDataAccessApiUsageException} when - * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@code null} for a - * particular element as Reactive Streams prohibit the usage of {@code null} values. + * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@literal null} for a + * particular element as Reactive Streams prohibit the usage of {@literal null} values. * * @author Mark Paluch * @author Jiahe Cai @@ -162,7 +162,7 @@ public interface ReactiveValueOperations { /** * Get multiple {@code keys}. Values are in the order of the requested keys. Absent field values are represented using - * {@code null} in the resulting {@link List}. + * {@literal null} in the resulting {@link List}. * * @param keys must not be {@literal null}. * @see Redis Documentation: MGET diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java index 552f008c24..1e77a2b1b4 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java @@ -37,8 +37,8 @@ *

    * Streams of methods returning {@code Mono} or {@code Flux} are terminated with * {@link org.springframework.dao.InvalidDataAccessApiUsageException} when - * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@code null} for a - * particular element as Reactive Streams prohibit the usage of {@code null} values. + * {@link org.springframework.data.redis.serializer.RedisElementReader#read(ByteBuffer)} returns {@literal null} for a + * particular element as Reactive Streams prohibit the usage of {@literal null} values. * * @author Mark Paluch * @author Christoph Strobl diff --git a/src/main/java/org/springframework/data/redis/core/RedisCallback.java b/src/main/java/org/springframework/data/redis/core/RedisCallback.java index 0c40d0702b..b506d6875a 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisCallback.java +++ b/src/main/java/org/springframework/data/redis/core/RedisCallback.java @@ -32,13 +32,13 @@ public interface RedisCallback { /** * Method called by {@link RedisTemplate} with an active {@link RedisConnection}. *

    - * Callback code need not care about activating/opening or closing the {@link RedisConnection}, - * nor handling {@link Exception exceptions}. + * Callback code need not care about activating/opening or closing the {@link RedisConnection}, nor handling + * {@link Exception exceptions}. * * @param connection active {@link RedisConnection Redis connection}. - * @return the {@link Object result} of the operation performed in the callback or {@code null}. + * @return the {@link Object result} of the operation performed in the callback or {@literal null}. * @throws DataAccessException if the operation performed by the callback fails to execute in the context of Redis - * using the given {@link RedisConnection}. + * using the given {@link RedisConnection}. */ @Nullable T doInRedis(RedisConnection connection) throws DataAccessException; diff --git a/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java b/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java index 767343557b..3996f3c982 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java +++ b/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java @@ -587,7 +587,7 @@ public RedisConnection getRequiredConnection() { * Override the existing {@link RedisConnection} handle with the given {@link RedisConnection}. Reset the handle if * given {@literal null}. *

    - * Used for releasing the Connection on suspend (with a {@code null} argument) and setting a fresh Connection on + * Used for releasing the Connection on suspend (with a {@literal null} argument) and setting a fresh Connection on * resume. */ protected void setConnection(@Nullable RedisConnection connection) { diff --git a/src/main/java/org/springframework/data/redis/core/ScanCursor.java b/src/main/java/org/springframework/data/redis/core/ScanCursor.java index 8c94ff726b..2779d17c46 100644 --- a/src/main/java/org/springframework/data/redis/core/ScanCursor.java +++ b/src/main/java/org/springframework/data/redis/core/ScanCursor.java @@ -86,7 +86,7 @@ public ScanCursor(CursorId cursorId) { * Crates new {@link ScanCursor} * * @param cursorId the cursor Id. - * @param options Defaulted to {@link ScanOptions#NONE} if {@code null}. + * @param options Defaulted to {@link ScanOptions#NONE} if {@literal null}. * @deprecated since 3.3.0 - Use {@link ScanCursor#ScanCursor(CursorId, ScanOptions)} instead. */ @Deprecated(since = "3.3.0") @@ -98,7 +98,7 @@ public ScanCursor(long cursorId, @Nullable ScanOptions options) { * Crates new {@link ScanCursor} * * @param cursorId the cursor Id. - * @param options Defaulted to {@link ScanOptions#NONE} if {@code null}. + * @param options Defaulted to {@link ScanOptions#NONE} if {@literal null}. * @since 3.3.0 */ public ScanCursor(CursorId cursorId, @Nullable ScanOptions options) { @@ -125,7 +125,7 @@ private void scan(CursorId cursorId) { /** * Performs the actual scan command using the native client implementation. The given {@literal options} are never - * {@code null}. + * {@literal null}. * * @param cursorId * @param options @@ -139,7 +139,7 @@ protected ScanIteration doScan(long cursorId, ScanOptions options) { /** * Performs the actual scan command using the native client implementation. The given {@literal options} are never - * {@code null}. + * {@literal null}. * * @param cursorId * @param options diff --git a/src/main/java/org/springframework/data/redis/core/ValueOperations.java b/src/main/java/org/springframework/data/redis/core/ValueOperations.java index 546a83f8ff..260bc7c4ca 100644 --- a/src/main/java/org/springframework/data/redis/core/ValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ValueOperations.java @@ -264,7 +264,7 @@ default Boolean setIfPresent(K key, V value, Duration timeout) { /** * Get multiple {@code keys}. Values are in the order of the requested keys Absent field values are represented using - * {@code null} in the resulting {@link List}. + * {@literal null} in the resulting {@link List}. * * @param keys must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. diff --git a/src/main/java/org/springframework/data/redis/core/convert/Bucket.java b/src/main/java/org/springframework/data/redis/core/convert/Bucket.java index 3599e8b5ee..01be61b6eb 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/Bucket.java +++ b/src/main/java/org/springframework/data/redis/core/convert/Bucket.java @@ -108,10 +108,10 @@ public byte[] get(String path) { } /** - * Return whether {@code path} is associated with a non-{@code null} value. + * Return whether {@code path} is associated with a non-{@literal null} value. * * @param path must not be {@literal null} or {@link String#isEmpty()}. - * @return {@literal true} if the {@code path} is associated with a non-{@code null} value. + * @return {@literal true} if the {@code path} is associated with a non-{@literal null} value. * @since 2.5 */ public boolean hasValue(String path) { diff --git a/src/test/java/org/springframework/data/redis/test/condition/RedisConditions.java b/src/test/java/org/springframework/data/redis/test/condition/RedisConditions.java index 26c5d1d29c..003ade86cd 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/RedisConditions.java +++ b/src/test/java/org/springframework/data/redis/test/condition/RedisConditions.java @@ -65,7 +65,7 @@ private RedisConditions(RedisClusterCommands commands) { /** * Create {@link RedisCommands} given {@link StatefulRedisConnection}. * - * @param connection must not be {@code null}. + * @param connection must not be {@literal null}. * @return */ public static RedisConditions of(StatefulRedisConnection connection) { @@ -75,7 +75,7 @@ public static RedisConditions of(StatefulRedisConnection connect /** * Create {@link RedisCommands} given {@link StatefulRedisClusterConnection}. * - * @param connection must not be {@code null}. + * @param connection must not be {@literal null}. * @return */ public static RedisConditions of(StatefulRedisClusterConnection connection) { @@ -85,7 +85,7 @@ public static RedisConditions of(StatefulRedisClusterConnection /** * Create {@link RedisConditions} given {@link RedisCommands}. * - * @param commands must not be {@code null}. + * @param commands must not be {@literal null}. * @return */ public static RedisConditions of(RedisClusterCommands commands) { From 047e63ea78c87b4285945454b8ee177c01853769 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Sun, 22 Sep 2024 16:50:02 +0900 Subject: [PATCH 082/187] `RedisNode` creation from bare hostname assigns default port. Closes #2928 Original pull request: #3002 --- .../data/redis/connection/RedisNode.java | 15 +++- .../redis/connection/RedisNodeUnitTests.java | 72 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java diff --git a/src/main/java/org/springframework/data/redis/connection/RedisNode.java b/src/main/java/org/springframework/data/redis/connection/RedisNode.java index 2ea0e6dd18..21202b76b0 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisNode.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisNode.java @@ -24,10 +24,13 @@ * @author Christoph Strobl * @author Thomas Darimont * @author Mark Paluch + * @author LeeHyungGeol * @since 1.4 */ public class RedisNode implements NamedNode { + private static final int DEFAULT_PORT = 6379; + @Nullable String id; @Nullable String name; @Nullable String host; @@ -94,7 +97,17 @@ public static RedisNode fromString(String hostPortString) { portString = hostPortString.substring(colonPos + 1); } else { // 0 or 2+ colons. Bare hostname or IPv6 literal. - host = hostPortString; + int lastColonIndex = hostPortString.lastIndexOf(':'); + + // IPv6 literal + if (lastColonIndex > hostPortString.indexOf(']')) { + host = hostPortString.substring(0, lastColonIndex); + portString = hostPortString.substring(lastColonIndex + 1); + } else { + // bare hostname + host = hostPortString; + portString = Integer.toString(DEFAULT_PORT); + } } } diff --git a/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java new file mode 100644 index 0000000000..90ea456652 --- /dev/null +++ b/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2014-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.connection; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +/** + * Unit tests for {@link RedisNode}. + * referred to the test code in ConversionUnitTests.java + * + * @author LeeHyungGeol + */ +public class RedisNodeUnitTests { + private static final int DEFAULT_PORT = 6379; + + @Test // GH-2928 + void shouldParseIPv4AddressWithPort() { + RedisNode node = RedisNode.fromString("127.0.0.1:6379"); + assertThat(node.getHost()).isEqualTo("127.0.0.1"); + assertThat(node.getPort()).isEqualTo(6379); + } + + @Test // GH-2928 + void shouldParseIPv6AddressWithPort() { + RedisNode node = RedisNode.fromString("[aaaa:bbbb::dddd:eeee]:6379"); + assertThat(node.getHost()).isEqualTo("aaaa:bbbb::dddd:eeee"); + assertThat(node.getPort()).isEqualTo(6379); + } + + @Test // GH-2928 + void shouldParseBareHostnameWithPort() { + RedisNode node = RedisNode.fromString("my.redis.server:6379"); + assertThat(node.getHost()).isEqualTo("my.redis.server"); + assertThat(node.getPort()).isEqualTo(6379); + } + + @Test // GH-2928 + void shouldThrowExceptionForInvalidPort() { + assertThatIllegalArgumentException() + .isThrownBy(() -> RedisNode.fromString("127.0.0.1:invalidPort")); + } + + @Test // GH-2928 + void shouldParseBareIPv6WithoutPort() { + RedisNode node = RedisNode.fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + assertThat(node.getHost()).isEqualTo("2001:0db8:85a3:0000:0000:8a2e:0370"); + assertThat(node.getPort()).isEqualTo(7334); + } + + @Test // GH-2928 + void shouldParseBareHostnameWithoutPort() { + RedisNode node = RedisNode.fromString("my.redis.server"); + assertThat(node.getHost()).isEqualTo("my.redis.server"); + assertThat(node.getPort()).isEqualTo(DEFAULT_PORT); + } +} + From 20ceb4cf0588e2650886ac8a53347cea68b95c73 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 10 Oct 2024 15:01:41 +0200 Subject: [PATCH 083/187] Polishing. Introduce factory method overload accepting the default port so that Sentinel uses the right port. Refine tests. Refine Javadoc. See #2928 Original pull request: #3002 --- .../connection/RedisClusterConfiguration.java | 4 +- .../data/redis/connection/RedisNode.java | 41 ++++++++++-- .../RedisSentinelConfiguration.java | 11 ++-- .../RedisClusterConfigurationUnitTests.java | 7 -- .../redis/connection/RedisNodeUnitTests.java | 66 ++++++++++++++----- .../RedisSentinelConfigurationUnitTests.java | 7 -- 6 files changed, 90 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java index 057527ed0d..62d9b6d140 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java @@ -62,11 +62,9 @@ public RedisClusterConfiguration() {} /** * Creates a new {@link RedisClusterConfiguration} for given {@link String hostPort} combinations. * - *

    -	 * 
    +	 * 
     	 * clusterHostAndPorts[0] = 127.0.0.1:23679
     	 * clusterHostAndPorts[1] = 127.0.0.1:23680 ...
    -	 * 
     	 * 
    * * @param clusterNodes must not be {@literal null}. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisNode.java b/src/main/java/org/springframework/data/redis/connection/RedisNode.java index 21202b76b0..fa04f94245 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisNode.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisNode.java @@ -29,7 +29,8 @@ */ public class RedisNode implements NamedNode { - private static final int DEFAULT_PORT = 6379; + public static final int DEFAULT_PORT = 6379; + public static final int DEFAULT_SENTINEL_PORT = 26379; @Nullable String id; @Nullable String name; @@ -69,8 +70,11 @@ private RedisNode(RedisNode redisNode) { * the port. For example: * *
    +	 * RedisNode.fromString("127.0.0.1");
     	 * RedisNode.fromString("127.0.0.1:6379");
    +	 * RedisNode.fromString("[aaaa:bbbb::dddd:eeee]");
     	 * RedisNode.fromString("[aaaa:bbbb::dddd:eeee]:6379");
    +	 * RedisNode.fromString("my.redis.server");
     	 * RedisNode.fromString("my.redis.server:6379");
     	 * 
    * @@ -79,6 +83,27 @@ private RedisNode(RedisNode redisNode) { * @since 2.7.4 */ public static RedisNode fromString(String hostPortString) { + return fromString(hostPortString, DEFAULT_PORT); + } + + /** + * Parse a {@code hostAndPort} string into {@link RedisNode}. Supports IPv4, IPv6, and hostname notations including + * the port. For example: + * + *
    +	 * RedisNode.fromString("127.0.0.1");
    +	 * RedisNode.fromString("127.0.0.1:6379");
    +	 * RedisNode.fromString("[aaaa:bbbb::dddd:eeee]");
    +	 * RedisNode.fromString("[aaaa:bbbb::dddd:eeee]:6379");
    +	 * RedisNode.fromString("my.redis.server");
    +	 * RedisNode.fromString("my.redis.server:6379");
    +	 * 
    + * + * @param hostPortString must not be {@literal null} or empty. + * @return the parsed {@link RedisNode}. + * @since 3.4 + */ + public static RedisNode fromString(String hostPortString, int defaultPort) { Assert.notNull(hostPortString, "HostAndPort must not be null"); @@ -106,16 +131,18 @@ public static RedisNode fromString(String hostPortString) { } else { // bare hostname host = hostPortString; - portString = Integer.toString(DEFAULT_PORT); } } } - int port = -1; - try { - port = Integer.parseInt(portString); - } catch (RuntimeException ignore) { - throw new IllegalArgumentException("Unparseable port number: %s".formatted(hostPortString)); + int port = defaultPort; + + if (StringUtils.hasText(portString)) { + try { + port = Integer.parseInt(portString); + } catch (RuntimeException ignore) { + throw new IllegalArgumentException("Unparseable port number: %s".formatted(hostPortString)); + } } if (!isValidPort(port)) { diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java index f9683193c6..f0dedc2e74 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java @@ -77,8 +77,9 @@ public RedisSentinelConfiguration() { /** * Creates a new {@link RedisSentinelConfiguration} for given {@link String hostPort} combinations. * - *
    -	 * sentinelHostAndPorts[0] = 127.0.0.1:23679 sentinelHostAndPorts[1] = 127.0.0.1:23680 ...
    +	 * 
    +	 * sentinelHostAndPorts[0] = 127.0.0.1:23679
    +	 * sentinelHostAndPorts[1] = 127.0.0.1:23680 ...
     	 * 
    * * @param sentinelHostAndPorts must not be {@literal null}. @@ -92,11 +93,9 @@ public RedisSentinelConfiguration(String master, Set sentinelHostAndPort * Creates a new {@link RedisSentinelConfiguration} looking up configuration values from the given * {@link PropertySource}. * - *
    -	 * 
    +	 * 
     	 * spring.redis.sentinel.master=myMaster
     	 * spring.redis.sentinel.nodes=127.0.0.1:23679,127.0.0.1:23680,127.0.0.1:23681
    -	 * 
     	 * 
    * * @param propertySource must not be {@literal null}. @@ -254,7 +253,7 @@ public RedisSentinelConfiguration sentinel(String host, Integer port) { private void appendSentinels(Set hostAndPorts) { for (String hostAndPort : hostAndPorts) { - addSentinel(RedisNode.fromString(hostAndPort)); + addSentinel(RedisNode.fromString(hostAndPort, RedisNode.DEFAULT_SENTINEL_PORT)); } } diff --git a/src/test/java/org/springframework/data/redis/connection/RedisClusterConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisClusterConfigurationUnitTests.java index 6d969d0876..fbd0d24da9 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisClusterConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisClusterConfigurationUnitTests.java @@ -41,7 +41,6 @@ class RedisClusterConfigurationUnitTests { private static final String HOST_AND_PORT_3 = "localhost:789"; private static final String HOST_AND_PORT_4 = "[fe80::a00:27ff:fe4b:ee48]:6379"; private static final String HOST_AND_PORT_5 = "[fe80:1234:1a2b:0:27ff:fe4b:0:ee48]:6380"; - private static final String HOST_AND_NO_PORT = "localhost"; @Test // DATAREDIS-315 void shouldCreateRedisClusterConfigurationCorrectly() { @@ -75,12 +74,6 @@ void shouldCreateRedisClusterConfigurationCorrectlyGivenMultipleHostAndPortStrin new RedisNode("localhost", 789)); } - @Test // DATAREDIS-315 - void shouldThrowExecptionOnInvalidHostAndPortString() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new RedisClusterConfiguration(Collections.singleton(HOST_AND_NO_PORT))); - } - @Test // DATAREDIS-315 void shouldThrowExceptionWhenListOfHostAndPortIsNull() { assertThatIllegalArgumentException().isThrownBy(() -> new RedisClusterConfiguration(Collections.singleton(null))); diff --git a/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java index 90ea456652..839f1132a5 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,58 +15,92 @@ */ package org.springframework.data.redis.connection; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + /** * Unit tests for {@link RedisNode}. - * referred to the test code in ConversionUnitTests.java * * @author LeeHyungGeol + * @author Mark Paluch */ -public class RedisNodeUnitTests { - private static final int DEFAULT_PORT = 6379; +class RedisNodeUnitTests { @Test // GH-2928 void shouldParseIPv4AddressWithPort() { - RedisNode node = RedisNode.fromString("127.0.0.1:6379"); + + RedisNode node = RedisNode.fromString("127.0.0.1:1234"); + assertThat(node.getHost()).isEqualTo("127.0.0.1"); - assertThat(node.getPort()).isEqualTo(6379); + assertThat(node.getPort()).isEqualTo(1234); + } + + @ParameterizedTest // GH-2928 + @ValueSource(strings = { "127.0.0.1", "127.0.0.1:" }) + void shouldParseIPv4AddressWithoutPort(String source) { + + RedisNode node = RedisNode.fromString(source); + + assertThat(node.getHost()).isEqualTo("127.0.0.1"); + assertThat(node.getPort()).isEqualTo(RedisNode.DEFAULT_PORT); } @Test // GH-2928 void shouldParseIPv6AddressWithPort() { - RedisNode node = RedisNode.fromString("[aaaa:bbbb::dddd:eeee]:6379"); + + RedisNode node = RedisNode.fromString("[aaaa:bbbb::dddd:eeee]:1234"); + assertThat(node.getHost()).isEqualTo("aaaa:bbbb::dddd:eeee"); - assertThat(node.getPort()).isEqualTo(6379); + assertThat(node.getPort()).isEqualTo(1234); + } + + @ParameterizedTest // GH-2928 + @ValueSource(strings = { "[aaaa:bbbb::dddd:eeee]", "[aaaa:bbbb::dddd:eeee]:" }) + void shouldParseIPv6AddressWithoutPort(String source) { + + RedisNode node = RedisNode.fromString(source); + + assertThat(node.getHost()).isEqualTo("aaaa:bbbb::dddd:eeee"); + assertThat(node.getPort()).isEqualTo(RedisNode.DEFAULT_PORT); } @Test // GH-2928 void shouldParseBareHostnameWithPort() { + RedisNode node = RedisNode.fromString("my.redis.server:6379"); + assertThat(node.getHost()).isEqualTo("my.redis.server"); assertThat(node.getPort()).isEqualTo(6379); } + @ParameterizedTest // GH-2928 + @ValueSource(strings = { "my.redis.server", "[my.redis.server:" }) + void shouldParseBareHostnameWithoutPort(String source) { + + RedisNode node = RedisNode.fromString("my.redis.server"); + + assertThat(node.getHost()).isEqualTo("my.redis.server"); + assertThat(node.getPort()).isEqualTo(RedisNode.DEFAULT_PORT); + } + @Test // GH-2928 void shouldThrowExceptionForInvalidPort() { + assertThatIllegalArgumentException() .isThrownBy(() -> RedisNode.fromString("127.0.0.1:invalidPort")); } @Test // GH-2928 void shouldParseBareIPv6WithoutPort() { + RedisNode node = RedisNode.fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + assertThat(node.getHost()).isEqualTo("2001:0db8:85a3:0000:0000:8a2e:0370"); assertThat(node.getPort()).isEqualTo(7334); } - @Test // GH-2928 - void shouldParseBareHostnameWithoutPort() { - RedisNode node = RedisNode.fromString("my.redis.server"); - assertThat(node.getHost()).isEqualTo("my.redis.server"); - assertThat(node.getPort()).isEqualTo(DEFAULT_PORT); - } } diff --git a/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java index 2b38c0a751..a8c42cb78f 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java @@ -41,7 +41,6 @@ class RedisSentinelConfigurationUnitTests { private static final String HOST_AND_PORT_1 = "127.0.0.1:123"; private static final String HOST_AND_PORT_2 = "localhost:456"; private static final String HOST_AND_PORT_3 = "localhost:789"; - private static final String HOST_AND_NO_PORT = "localhost"; @Test // DATAREDIS-372 void shouldCreateRedisSentinelConfigurationCorrectlyGivenMasterAndSingleHostAndPortString() { @@ -74,12 +73,6 @@ void shouldCreateRedisSentinelConfigurationCorrectlyGivenMasterAndMultipleHostAn new RedisNode("localhost", 789)); } - @Test // DATAREDIS-372 - void shouldThrowExecptionOnInvalidHostAndPortString() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new RedisSentinelConfiguration("mymaster", Collections.singleton(HOST_AND_NO_PORT))); - } - @Test // DATAREDIS-372 void shouldThrowExceptionWhenListOfHostAndPortIsNull() { assertThatIllegalArgumentException() From b1453dabc030fa746acb82d41a0b5ccd8da14994 Mon Sep 17 00:00:00 2001 From: "sujl95(TheWing)" Date: Mon, 30 Sep 2024 01:30:27 +0900 Subject: [PATCH 084/187] =?UTF-8?q?Correctly=20handle=20`null`=20listener?= =?UTF-8?q?=20in=20`RedisMessageListenerContainer.remove(=E2=80=A6)`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #3009 --- .../RedisMessageListenerContainer.java | 47 ++++++++++--------- ...edisMessageListenerContainerUnitTests.java | 36 ++++++++++++-- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java index 8008f8d7c3..225a3ac81b 100644 --- a/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java @@ -101,6 +101,7 @@ * @author Thomas Darimont * @author Mark Paluch * @author John Blum + * @author SEONGJUN LEE * @see MessageListener * @see SubscriptionListener */ @@ -770,33 +771,35 @@ else if (isListening()) { } private void remove(@Nullable MessageListener listener, Topic topic, ByteArrayWrapper holder, - Map> mapping, List topicToRemove) { + Map> mapping, List topicToRemove) { Collection listeners = mapping.get(holder); - Collection listenersToRemove = null; - - if (listeners != null) { - // remove only one listener - listeners.remove(listener); - listenersToRemove = Collections.singletonList(listener); - - // start removing listeners - for (MessageListener messageListener : listenersToRemove) { - Set topics = listenerTopics.get(messageListener); - if (topics != null) { - topics.remove(topic); - } - if (CollectionUtils.isEmpty(topics)) { - listenerTopics.remove(messageListener); - } - } + if (listeners == null || listeners.isEmpty()) { + return; + } - // if we removed everything, remove the empty holder collection - if (listeners.isEmpty()) { - mapping.remove(holder); - topicToRemove.add(holder.getArray()); + Collection listenersToRemove = (listener == null) ? new ArrayList<>(listeners) + : Collections.singletonList(listener); + + // Remove the specified listener(s) from the original collection + listeners.removeAll(listenersToRemove); + + // Start removing listeners + for (MessageListener messageListener : listenersToRemove) { + Set topics = listenerTopics.get(messageListener); + if (topics != null) { + topics.remove(topic); + } + if (CollectionUtils.isEmpty(topics)) { + listenerTopics.remove(messageListener); } } + + // If all listeners were removed, clean up the mapping and the holder + if (listeners.isEmpty()) { + mapping.remove(holder); + topicToRemove.add(holder.getArray()); + } } private Subscriber createSubscriber(RedisConnectionFactory connectionFactory, Executor executor) { diff --git a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java index 7d71909c71..d953353253 100644 --- a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java @@ -19,6 +19,7 @@ import static org.mockito.Mockito.*; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; @@ -30,10 +31,7 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.core.task.SyncTaskExecutor; import org.springframework.data.redis.RedisConnectionFailureException; -import org.springframework.data.redis.connection.RedisConnection; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.Subscription; -import org.springframework.data.redis.connection.SubscriptionListener; +import org.springframework.data.redis.connection.*; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import org.springframework.data.redis.listener.adapter.RedisListenerExecutionFailedException; @@ -221,4 +219,34 @@ void shouldRecoverFromConnectionFailure() throws Exception { void failsOnDuplicateInit() { assertThatIllegalStateException().isThrownBy(() -> container.afterPropertiesSet()); } + + @Test + void shouldRemoveSpecificListenerFromMappingAndListenerTopics() { + MessageListener listener1 = mock(MessageListener.class); + MessageListener listener2 = mock(MessageListener.class); + Topic topic = new ChannelTopic("topic1"); + + container.addMessageListener(listener1, Collections.singletonList(topic)); + container.addMessageListener(listener2, Collections.singletonList(topic)); + + container.removeMessageListener(listener1, Collections.singletonList(topic)); + + container.addMessageListener(listener2, Collections.singletonList(topic)); + verify(listener1, never()).onMessage(any(), any()); + } + + @Test + void shouldRemoveAllListenersWhenListenerIsNull() { + MessageListener listener1 = mock(MessageListener.class); + MessageListener listener2 = mock(MessageListener.class); + Topic topic = new ChannelTopic("topic1"); + + container.addMessageListener(listener1, Collections.singletonList(topic)); + container.addMessageListener(listener2, Collections.singletonList(topic)); + + container.removeMessageListener(null, Collections.singletonList(topic)); + + verify(listener1, never()).onMessage(any(), any()); + verify(listener2, never()).onMessage(any(), any()); + } } From 3a9249b9fe199355e601bec8a05a5cd5207b2952 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 10 Oct 2024 15:22:11 +0200 Subject: [PATCH 085/187] Polishing. Simplify code and tests. See #3009 --- .../RedisMessageListenerContainer.java | 22 +++++++-------- ...edisMessageListenerContainerUnitTests.java | 28 ++++++------------- 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java index 225a3ac81b..b180a6bfd9 100644 --- a/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java @@ -101,7 +101,7 @@ * @author Thomas Darimont * @author Mark Paluch * @author John Blum - * @author SEONGJUN LEE + * @author Seongjun Lee * @see MessageListener * @see SubscriptionListener */ @@ -555,8 +555,8 @@ public final boolean isActive() { * Adds a message listener to the (potentially running) container. If the container is running, the listener starts * receiving (matching) messages as soon as possible. * - * @param listener message listener - * @param topics message listener topic + * @param listener message listener. + * @param topics message listener topic. */ public void addMessageListener(MessageListener listener, Collection topics) { addListener(listener, topics); @@ -566,8 +566,8 @@ public void addMessageListener(MessageListener listener, Collection topics) { removeListener(listener, topics); @@ -594,8 +594,8 @@ public void removeMessageListener(@Nullable MessageListener listener, Collection * Note that this method obeys the Redis (p)unsubscribe semantics - meaning an empty/null collection will remove * listener from all channels. * - * @param listener message listener - * @param topic message topic + * @param listener message listener. + * @param topic message topic. */ public void removeMessageListener(@Nullable MessageListener listener, Topic topic) { removeMessageListener(listener, Collections.singleton(topic)); @@ -605,7 +605,7 @@ public void removeMessageListener(@Nullable MessageListener listener, Topic topi * Removes the given message listener completely (from all topics). If the container is running, the listener stops * receiving (matching) messages as soon as possible. * - * @param listener message listener + * @param listener message listener. */ public void removeMessageListener(MessageListener listener) { @@ -774,7 +774,7 @@ private void remove(@Nullable MessageListener listener, Topic topic, ByteArrayWr Map> mapping, List topicToRemove) { Collection listeners = mapping.get(holder); - if (listeners == null || listeners.isEmpty()) { + if (CollectionUtils.isEmpty(listeners)) { return; } diff --git a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java index d953353253..06a9f567c1 100644 --- a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java @@ -31,7 +31,11 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.core.task.SyncTaskExecutor; import org.springframework.data.redis.RedisConnectionFailureException; -import org.springframework.data.redis.connection.*; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.Subscription; +import org.springframework.data.redis.connection.SubscriptionListener; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import org.springframework.data.redis.listener.adapter.RedisListenerExecutionFailedException; @@ -42,6 +46,7 @@ * * @author Mark Paluch * @author Christoph Strobl + * @author Seongjun Lee */ class RedisMessageListenerContainerUnitTests { @@ -220,23 +225,9 @@ void failsOnDuplicateInit() { assertThatIllegalStateException().isThrownBy(() -> container.afterPropertiesSet()); } - @Test - void shouldRemoveSpecificListenerFromMappingAndListenerTopics() { - MessageListener listener1 = mock(MessageListener.class); - MessageListener listener2 = mock(MessageListener.class); - Topic topic = new ChannelTopic("topic1"); - - container.addMessageListener(listener1, Collections.singletonList(topic)); - container.addMessageListener(listener2, Collections.singletonList(topic)); - - container.removeMessageListener(listener1, Collections.singletonList(topic)); - - container.addMessageListener(listener2, Collections.singletonList(topic)); - verify(listener1, never()).onMessage(any(), any()); - } - - @Test + @Test // GH-3009 void shouldRemoveAllListenersWhenListenerIsNull() { + MessageListener listener1 = mock(MessageListener.class); MessageListener listener2 = mock(MessageListener.class); Topic topic = new ChannelTopic("topic1"); @@ -246,7 +237,6 @@ void shouldRemoveAllListenersWhenListenerIsNull() { container.removeMessageListener(null, Collections.singletonList(topic)); - verify(listener1, never()).onMessage(any(), any()); - verify(listener2, never()).onMessage(any(), any()); + assertThatNoException().isThrownBy(() -> container.removeMessageListener(null, Collections.singletonList(topic))); } } From 83cb3d87d1c5e6d147bf7bcfa0fe6d1cdaf97669 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 10 Oct 2024 15:32:26 +0200 Subject: [PATCH 086/187] Fix Jedis cluster connection lookup for IPv6 hosts. We previously used our own mechanism to Render Host and Port causing that IPv6 addresses were enclosed in brackets. Now we've aligned with Jedis' keying by using HostAndPort that just concatenates the host and port part. Closes #3015 --- .../connection/jedis/JedisClusterConnection.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java index b4b2d8422b..9dc9af5cbb 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java @@ -20,6 +20,8 @@ import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; +import redis.clients.jedis.JedisClusterInfoCache; +import redis.clients.jedis.Protocol; import redis.clients.jedis.providers.ClusterConnectionProvider; import java.time.Duration; @@ -764,11 +766,16 @@ public Jedis getResourceForSpecificNode(RedisClusterNode node) { throw new DataAccessResourceFailureException("Node %s is unknown to cluster".formatted(node)); } + @Nullable private ConnectionPool getResourcePoolForSpecificNode(RedisClusterNode node) { Map clusterNodes = cluster.getClusterNodes(); - if (clusterNodes.containsKey(node.asString())) { - return clusterNodes.get(node.asString()); + HostAndPort hap = new HostAndPort(node.getHost(), + node.getPort() == null ? Protocol.DEFAULT_PORT : node.getPort()); + String key = JedisClusterInfoCache.getNodeKey(hap); + + if (clusterNodes.containsKey(key)) { + return clusterNodes.get(key); } return null; From 12d97be97f321b50a3179509db6312ffd889dd4e Mon Sep 17 00:00:00 2001 From: Ehsan Alemzadeh Date: Wed, 24 Apr 2024 10:09:00 +0330 Subject: [PATCH 087/187] Use extended `SET` instead of deprecated setEx, pSetEx and setNX commands. Replace usage of deprecated commands setEx, pSetEx and setNX in DefaultValueOperations by set command with additional SetOption arguments Closes #2897 Original pull request: #2900 --- .../redis/core/DefaultValueOperations.java | 35 ++----------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java index 9938d5a0ec..c3d3e00e54 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java @@ -22,8 +22,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; - -import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.BitFieldSubCommands; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisStringCommands.SetOption; @@ -37,6 +35,7 @@ * @author Jennifer Hickey * @author Christoph Strobl * @author Jiahe Cai + * @author Ehsan Alemzadeh */ class DefaultValueOperations extends AbstractOperations implements ValueOperations { @@ -250,35 +249,7 @@ public void set(K key, V value, long timeout, TimeUnit unit) { byte[] rawKey = rawKey(key); byte[] rawValue = rawValue(value); - execute(new RedisCallback() { - - @Override - public Object doInRedis(RedisConnection connection) throws DataAccessException { - - potentiallyUsePsetEx(connection); - return null; - } - - public void potentiallyUsePsetEx(RedisConnection connection) { - - if (!TimeUnit.MILLISECONDS.equals(unit) || !failsafeInvokePsetEx(connection)) { - connection.setEx(rawKey, TimeoutUtils.toSeconds(timeout, unit), rawValue); - } - } - - private boolean failsafeInvokePsetEx(RedisConnection connection) { - - boolean failed = false; - try { - connection.pSetEx(rawKey, timeout, rawValue); - } catch (UnsupportedOperationException ignore) { - // in case the connection does not support pSetEx return false to allow fallback to other operation. - failed = true; - } - return !failed; - } - - }); + execute(connection -> connection.set(rawKey, rawValue, Expiration.from(timeout, unit), SetOption.upsert())); } @Override @@ -286,7 +257,7 @@ public Boolean setIfAbsent(K key, V value) { byte[] rawKey = rawKey(key); byte[] rawValue = rawValue(value); - return execute(connection -> connection.setNX(rawKey, rawValue)); + return execute(connection -> connection.set(rawKey, rawValue, Expiration.persistent(), SetOption.ifAbsent())); } @Override From b1e07f5e15ed9dd13dd2b596ef85a37853795695 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 11 Oct 2024 10:25:41 +0200 Subject: [PATCH 088/187] Polishing. Replace subclass per command usage with lambdas. See #2897 Original pull request: #2900 --- .../data/redis/core/AbstractOperations.java | 22 ++++++- .../redis/core/DefaultValueOperations.java | 66 ++++--------------- 2 files changed, 32 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/AbstractOperations.java b/src/main/java/org/springframework/data/redis/core/AbstractOperations.java index d3f2a080d4..3d0832e93b 100644 --- a/src/main/java/org/springframework/data/redis/core/AbstractOperations.java +++ b/src/main/java/org/springframework/data/redis/core/AbstractOperations.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.BiFunction; import org.springframework.data.geo.GeoResults; import org.springframework.data.redis.connection.RedisConnection; @@ -51,7 +52,7 @@ abstract class AbstractOperations { // utility methods for the template internal methods abstract class ValueDeserializingRedisCallback implements RedisCallback { - private Object key; + private final Object key; public ValueDeserializingRedisCallback(Object key) { this.key = key; @@ -66,12 +67,31 @@ public final V doInRedis(RedisConnection connection) { protected abstract byte[] inRedis(byte[] rawKey, RedisConnection connection); } + private class FunctionalValueDeserializingRedisCallback extends ValueDeserializingRedisCallback { + + private final BiFunction function; + + public FunctionalValueDeserializingRedisCallback(Object key, BiFunction function) { + super(key); + this.function = function; + } + + @Nullable + protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { + return function.apply(connection, rawKey); + } + } + final RedisTemplate template; AbstractOperations(RedisTemplate template) { this.template = template; } + ValueDeserializingRedisCallback valueCallbackFor(Object key, BiFunction function) { + return new FunctionalValueDeserializingRedisCallback(key, function); + } + RedisSerializer keySerializer() { return template.getKeySerializer(); } diff --git a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java index c3d3e00e54..6b857ee598 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java @@ -22,8 +22,9 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; + import org.springframework.data.redis.connection.BitFieldSubCommands; -import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.DefaultedRedisConnection; import org.springframework.data.redis.connection.RedisStringCommands.SetOption; import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; @@ -45,79 +46,39 @@ class DefaultValueOperations extends AbstractOperations implements V @Override public V get(Object key) { - - return execute(new ValueDeserializingRedisCallback(key) { - - @Override - protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { - return connection.get(rawKey); - } - }); + return execute(valueCallbackFor(key, DefaultedRedisConnection::get)); } @Nullable @Override public V getAndDelete(K key) { - - return execute(new ValueDeserializingRedisCallback(key) { - - @Override - protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { - return connection.getDel(rawKey); - } - }); + return execute(valueCallbackFor(key, DefaultedRedisConnection::getDel)); } @Nullable @Override public V getAndExpire(K key, long timeout, TimeUnit unit) { - - return execute(new ValueDeserializingRedisCallback(key) { - - @Override - protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { - return connection.getEx(rawKey, Expiration.from(timeout, unit)); - } - }); + return execute( + valueCallbackFor(key, (connection, rawKey) -> connection.getEx(rawKey, Expiration.from(timeout, unit)))); } @Nullable @Override public V getAndExpire(K key, Duration timeout) { - - return execute(new ValueDeserializingRedisCallback(key) { - - @Override - protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { - return connection.getEx(rawKey, Expiration.from(timeout)); - } - }); + return execute(valueCallbackFor(key, (connection, rawKey) -> connection.getEx(rawKey, Expiration.from(timeout)))); } @Nullable @Override public V getAndPersist(K key) { - - return execute(new ValueDeserializingRedisCallback(key) { - - @Override - protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { - return connection.getEx(rawKey, Expiration.persistent()); - } - }); + return execute(valueCallbackFor(key, (connection, rawKey) -> connection.getEx(rawKey, Expiration.persistent()))); } @Override public V getAndSet(K key, V newValue) { byte[] rawValue = rawValue(newValue); - return execute(new ValueDeserializingRedisCallback(key) { - - @Override - protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { - return connection.getSet(rawKey, rawValue); - } - }); + return execute(valueCallbackFor(key, (connection, rawKey) -> connection.getSet(rawKey, rawValue))); } @Override @@ -232,15 +193,10 @@ public Boolean multiSetIfAbsent(Map m) { @Override public void set(K key, V value) { + byte[] rawKey = rawKey(key); byte[] rawValue = rawValue(value); - execute(new ValueDeserializingRedisCallback(key) { - @Override - protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { - connection.set(rawKey, rawValue); - return null; - } - }); + execute(connection -> connection.set(rawKey, rawValue)); } @Override From 856776710e1c19334e5d330d25b7c255b3b910f9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 17 Oct 2024 15:43:18 +0200 Subject: [PATCH 089/187] Polishing. Fix Javadoc tags. See #2994 --- .../data/redis/cache/RedisCacheConfiguration.java | 1 - .../org/springframework/data/redis/hash/Jackson2HashMapper.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java index a8bf4cd44a..0d32f86a0d 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java @@ -436,7 +436,6 @@ public void configureKeyConverters(Consumer registryConsumer) * Registers default cache {@link Converter key converters}. *

    * The following converters get registered: - *

    *

      *
    • {@link String} to {@link byte byte[]} using UTF-8 encoding.
    • *
    • {@link SimpleKey} to {@link String}
    • diff --git a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java index b1eb838734..ea9c5d24b4 100644 --- a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java @@ -65,7 +65,6 @@ * Flattening requires all property names to not interfere with JSON paths. Using dots or brackets in map keys or as * property names is not supported using flattening. The resulting hash cannot be mapped back into an Object. *

      Example

      - *

      *

        * class Person {
        * 	String firstname;
      
      From b18829ffc2507b5c5e03c6b000e78c601b99e505 Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Fri, 18 Oct 2024 12:46:44 +0200
      Subject: [PATCH 090/187] Prepare 3.4 RC1 (2024.1.0).
      
      See #2994
      ---
       pom.xml                       | 16 +++-------------
       src/main/resources/notice.txt |  3 ++-
       2 files changed, 5 insertions(+), 14 deletions(-)
      
      diff --git a/pom.xml b/pom.xml
      index b20be3b3a5..de40db469d 100644
      --- a/pom.xml
      +++ b/pom.xml
      @@ -14,12 +14,12 @@
       	
       		org.springframework.data.build
       		spring-data-parent
      -		3.4.0-SNAPSHOT
      +		3.4.0-RC1
       	
       
       	
      -		3.4.0-SNAPSHOT
      -		3.4.0-SNAPSHOT
      +		3.4.0-RC1
      +		3.4.0-RC1
       		4.0.2
       		1.9.4
       		1.4.20
      @@ -383,16 +383,6 @@
       	
       
       	
      -		
      -			spring-snapshot
      -			https://repo.spring.io/snapshot
      -			
      -				true
      -			
      -			
      -				false
      -			
      -		
       		
       			spring-milestone
       			https://repo.spring.io/milestone
      diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt
      index a546af9c6d..906935eb43 100644
      --- a/src/main/resources/notice.txt
      +++ b/src/main/resources/notice.txt
      @@ -1,4 +1,4 @@
      -Spring Data Redis 3.4 M1 (2024.1.0)
      +Spring Data Redis 3.4 RC1 (2024.1.0)
       Copyright (c) [2010-2019] Pivotal Software, Inc.
       
       This product is licensed to you under the Apache License, Version 2.0 (the "License").
      @@ -54,5 +54,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
       
       
       
      +
       
       
      
      From 2a1811fea3b51fa1f63652a6070556be23e55890 Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Fri, 18 Oct 2024 12:47:01 +0200
      Subject: [PATCH 091/187] Release version 3.4 RC1 (2024.1.0).
      
      See #2994
      ---
       pom.xml | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/pom.xml b/pom.xml
      index de40db469d..8f198b35c8 100644
      --- a/pom.xml
      +++ b/pom.xml
      @@ -5,7 +5,7 @@
       
       	org.springframework.data
       	spring-data-redis
      -	3.4.0-SNAPSHOT
      +	3.4.0-RC1
       
       	Spring Data Redis
       	Spring Data module for Redis
      
      From 55cbf020b6817a1d7a64123dbe337716a6ecbd38 Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Fri, 18 Oct 2024 12:49:40 +0200
      Subject: [PATCH 092/187] Prepare next development iteration.
      
      See #2994
      ---
       pom.xml | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/pom.xml b/pom.xml
      index 8f198b35c8..de40db469d 100644
      --- a/pom.xml
      +++ b/pom.xml
      @@ -5,7 +5,7 @@
       
       	org.springframework.data
       	spring-data-redis
      -	3.4.0-RC1
      +	3.4.0-SNAPSHOT
       
       	Spring Data Redis
       	Spring Data module for Redis
      
      From 976a82e6d54a8e7f23a06dbcdd43257ae01ea1fd Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Fri, 18 Oct 2024 12:49:41 +0200
      Subject: [PATCH 093/187] After release cleanups.
      
      See #2994
      ---
       pom.xml | 16 +++++++++++++---
       1 file changed, 13 insertions(+), 3 deletions(-)
      
      diff --git a/pom.xml b/pom.xml
      index de40db469d..b20be3b3a5 100644
      --- a/pom.xml
      +++ b/pom.xml
      @@ -14,12 +14,12 @@
       	
       		org.springframework.data.build
       		spring-data-parent
      -		3.4.0-RC1
      +		3.4.0-SNAPSHOT
       	
       
       	
      -		3.4.0-RC1
      -		3.4.0-RC1
      +		3.4.0-SNAPSHOT
      +		3.4.0-SNAPSHOT
       		4.0.2
       		1.9.4
       		1.4.20
      @@ -383,6 +383,16 @@
       	
       
       	
      +		
      +			spring-snapshot
      +			https://repo.spring.io/snapshot
      +			
      +				true
      +			
      +			
      +				false
      +			
      +		
       		
       			spring-milestone
       			https://repo.spring.io/milestone
      
      From 88b3d66c15abe915b86c2e8fb4ef0a28346a3e21 Mon Sep 17 00:00:00 2001
      From: "Kyle J. Davis" 
      Date: Fri, 1 Nov 2024 07:56:19 -0600
      Subject: [PATCH 094/187] Adds explicit mention of Valkey in the README.
      
      Signed-off-by: Kyle J. Davis 
      
      Closes #3032
      ---
       README.adoc | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/README.adoc b/README.adoc
      index ea031b6843..c2046b018e 100644
      --- a/README.adoc
      +++ b/README.adoc
      @@ -2,7 +2,7 @@
       
       The primary goal of the https://spring.io/projects/spring-data/[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
       
      -This module provides integration with the https://redis.io/[Redis] store.
      +This module provides integration with the https://redis.io/[Redis] store. It is also tested to work with https://valkey.io/[Valkey].
       
       == Features
       
      
      From a36e106cdfaf166533bb15be15d465e59f2d04d3 Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Thu, 7 Nov 2024 09:42:32 +0100
      Subject: [PATCH 095/187] Polishing.
      
      Mention best-effort testing.
      
      See #3032
      ---
       README.adoc | 3 ++-
       1 file changed, 2 insertions(+), 1 deletion(-)
      
      diff --git a/README.adoc b/README.adoc
      index c2046b018e..b62eb74359 100644
      --- a/README.adoc
      +++ b/README.adoc
      @@ -2,7 +2,8 @@
       
       The primary goal of the https://spring.io/projects/spring-data/[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
       
      -This module provides integration with the https://redis.io/[Redis] store. It is also tested to work with https://valkey.io/[Valkey].
      +This module provides integration with the https://redis.io/[Redis] store.
      +It is also tested to work with https://valkey.io/[Valkey] on a best-effort basis as long as Valkey remains largely compatible with Redis.
       
       == Features
       
      
      From dc0fcb86d351c3acbcca82abc82f2c933592b6db Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Thu, 7 Nov 2024 09:47:27 +0100
      Subject: [PATCH 096/187] Upgrade to Maven Wrapper 3.9.9.
      
      See #3034
      ---
       .mvn/wrapper/maven-wrapper.properties | 4 ++--
       1 file changed, 2 insertions(+), 2 deletions(-)
      
      diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
      index e0f6d3f781..f15f39b88f 100755
      --- a/.mvn/wrapper/maven-wrapper.properties
      +++ b/.mvn/wrapper/maven-wrapper.properties
      @@ -1,2 +1,2 @@
      -#Thu Aug 08 10:22:08 CEST 2024
      -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip
      +#Thu Nov 07 09:47:27 CET 2024
      +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
      
      From dc07365aa760ad94290cee9a3be9b2adfe9cf7af Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Thu, 7 Nov 2024 09:56:12 +0100
      Subject: [PATCH 097/187] Update CI Properties.
      
      See #3025
      ---
       ci/pipeline.properties | 1 +
       1 file changed, 1 insertion(+)
      
      diff --git a/ci/pipeline.properties b/ci/pipeline.properties
      index 40bb349196..c342f49a24 100644
      --- a/ci/pipeline.properties
      +++ b/ci/pipeline.properties
      @@ -11,6 +11,7 @@ docker.mongodb.4.4.version=4.4.25
       docker.mongodb.5.0.version=5.0.21
       docker.mongodb.6.0.version=6.0.10
       docker.mongodb.7.0.version=7.0.2
      +docker.mongodb.8.0.version=8.0.0
       
       # Supported versions of Redis
       docker.redis.6.version=6.2.13
      
      From 4d9443568721390a14c343f3258e6ae6efbbb853 Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Thu, 7 Nov 2024 09:57:36 +0100
      Subject: [PATCH 098/187] Update CI Properties.
      
      See #3025
      ---
       ci/pipeline.properties | 4 ++--
       1 file changed, 2 insertions(+), 2 deletions(-)
      
      diff --git a/ci/pipeline.properties b/ci/pipeline.properties
      index c342f49a24..cd2fcf7fbe 100644
      --- a/ci/pipeline.properties
      +++ b/ci/pipeline.properties
      @@ -1,6 +1,6 @@
       # Java versions
      -java.main.tag=17.0.12_7-jdk-focal
      -java.next.tag=22.0.2_9-jdk-jammy
      +java.main.tag=17.0.13_11-jdk-focal
      +java.next.tag=23.0.1_11-jdk-noble
       
       # Docker container images - standard
       docker.java.main.image=library/eclipse-temurin:${java.main.tag}
      
      From d3845ddf0ee1167a83d87fb6d8d12923948d1aeb Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Thu, 7 Nov 2024 16:38:56 +0100
      Subject: [PATCH 099/187] Update Dockerfile setup to use HTTPS sources.
      
      Closes #3037
      ---
       ci/openjdk17-redis-6.2/Dockerfile  | 5 ++++-
       ci/openjdk17-redis-7.2/Dockerfile  | 5 ++++-
       ci/openjdk17-valkey-7.2/Dockerfile | 5 ++++-
       ci/openjdk21-redis-6.2/Dockerfile  | 8 +++++---
       4 files changed, 17 insertions(+), 6 deletions(-)
      
      diff --git a/ci/openjdk17-redis-6.2/Dockerfile b/ci/openjdk17-redis-6.2/Dockerfile
      index d20826b6cb..bf7cb4d390 100644
      --- a/ci/openjdk17-redis-6.2/Dockerfile
      +++ b/ci/openjdk17-redis-6.2/Dockerfile
      @@ -8,7 +8,10 @@ ENV VERSION=${VERSION}
       COPY ./Makefile /
       
       RUN set -eux; \
      -#	sed -i -e 's/http/https/g' /etc/apt/sources.list ; \
      +	sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
      +	sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
      +	sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \
      +	sed -i -e 's/http/https/g' /etc/apt/sources.list && \
       	apt-get update ; \
       	apt-get install -y build-essential ; \
       	make work/redis/bin/redis-cli work/redis/bin/redis-server VERSION=${VERSION}; \
      diff --git a/ci/openjdk17-redis-7.2/Dockerfile b/ci/openjdk17-redis-7.2/Dockerfile
      index d20826b6cb..bf7cb4d390 100644
      --- a/ci/openjdk17-redis-7.2/Dockerfile
      +++ b/ci/openjdk17-redis-7.2/Dockerfile
      @@ -8,7 +8,10 @@ ENV VERSION=${VERSION}
       COPY ./Makefile /
       
       RUN set -eux; \
      -#	sed -i -e 's/http/https/g' /etc/apt/sources.list ; \
      +	sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
      +	sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
      +	sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \
      +	sed -i -e 's/http/https/g' /etc/apt/sources.list && \
       	apt-get update ; \
       	apt-get install -y build-essential ; \
       	make work/redis/bin/redis-cli work/redis/bin/redis-server VERSION=${VERSION}; \
      diff --git a/ci/openjdk17-valkey-7.2/Dockerfile b/ci/openjdk17-valkey-7.2/Dockerfile
      index 1b89b923ae..2c7dfcbce4 100644
      --- a/ci/openjdk17-valkey-7.2/Dockerfile
      +++ b/ci/openjdk17-valkey-7.2/Dockerfile
      @@ -10,7 +10,10 @@ ENV GH_ORG=valkey-io
       COPY ./Makefile /
       
       RUN set -eux; \
      -#	sed -i -e 's/http/https/g' /etc/apt/sources.list ; \
      +	sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
      +	sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
      +	sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \
      +	sed -i -e 's/http/https/g' /etc/apt/sources.list && \
       	apt-get update ; \
       	apt-get install -y build-essential ; \
       	make work/valkey/bin/valkey-cli work/valkey/bin/valkey-server VERSION=${VERSION}; \
      diff --git a/ci/openjdk21-redis-6.2/Dockerfile b/ci/openjdk21-redis-6.2/Dockerfile
      index ea08b24bec..a92d2b8fd6 100644
      --- a/ci/openjdk21-redis-6.2/Dockerfile
      +++ b/ci/openjdk21-redis-6.2/Dockerfile
      @@ -7,11 +7,13 @@ ENV VERSION=${VERSION}
       # Copy Spring Data Redis's Makefile into the container
       COPY ./Makefile /
       
      -
       RUN set -eux; \
      -#	sed -i -e 's/http/https/g' /etc/apt/sources.list ; \
      +	sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
      +	sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
      +	sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \
      +	sed -i -e 's/http/https/g' /etc/apt/sources.list && \
       	apt-get update ; \
      -	apt-get install -y build-essential curl; \
      +	apt-get install -y build-essential curl ; \
       	make work/redis/bin/redis-cli work/redis/bin/redis-server VERSION=${VERSION}; \
       	chmod -R o+rw work; \
       	apt-get clean; \
      
      From 0c0a5d8787eb8dd98e13118eb5623df67d33ee99 Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Fri, 8 Nov 2024 09:47:35 +0100
      Subject: [PATCH 100/187] Upgrade to xstream 1.4.21.
      
      Closes #3038
      ---
       pom.xml | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/pom.xml b/pom.xml
      index b20be3b3a5..65873fdade 100644
      --- a/pom.xml
      +++ b/pom.xml
      @@ -22,7 +22,7 @@
       		3.4.0-SNAPSHOT
       		4.0.2
       		1.9.4
      -		1.4.20
      +		1.4.21
       		2.11.1
       		6.4.0.RELEASE
       		5.2.0
      
      From 10cf5d48e9728b41fbddf680f5370089a24506a9 Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Thu, 14 Nov 2024 15:31:23 +0100
      Subject: [PATCH 101/187] Upgrade to Lettuce 6.4.1.RELEASE.
      
      Closes #3041
      ---
       pom.xml | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/pom.xml b/pom.xml
      index 65873fdade..7fa8827058 100644
      --- a/pom.xml
      +++ b/pom.xml
      @@ -24,7 +24,7 @@
       		1.9.4
       		1.4.21
       		2.11.1
      -		6.4.0.RELEASE
      +		6.4.1.RELEASE
       		5.2.0
       		1.01
       		4.1.107.Final
      
      From 18b43f27f6378276feab4559df4fe0fbfe1399ce Mon Sep 17 00:00:00 2001
      From: Mark Paluch 
      Date: Thu, 14 Nov 2024 15:42:41 +0100
      Subject: [PATCH 102/187] Bump Netty test dependency versions to 4.1.115.Final.
      
      See #3041
      ---
       pom.xml | 12 +++++++++---
       1 file changed, 9 insertions(+), 3 deletions(-)
      
      diff --git a/pom.xml b/pom.xml
      index 7fa8827058..46cd6259e4 100644
      --- a/pom.xml
      +++ b/pom.xml
      @@ -27,7 +27,7 @@
       		6.4.1.RELEASE
       		5.2.0
       		1.01
      -		4.1.107.Final
      +		4.1.115.Final
       		spring.data.redis
       	
       
      @@ -51,6 +51,14 @@
       				${pool}
       			
       
      +			
      +				io.netty
      +				netty-bom
      +				${netty}
      +				pom
      +				import
      +			
      +
       		
       	
       
      @@ -104,7 +112,6 @@
       			io.netty
       			netty-transport-native-epoll
       			linux-x86_64
      -			${netty}
       			test
       		
       
      @@ -112,7 +119,6 @@
       			io.netty
       			netty-transport-native-kqueue
       			osx-x86_64
      -			${netty}
       			test
       		
       
      
      From e2746ecf5af8e87ca193af8c2ac96bc918e9ba7e Mon Sep 17 00:00:00 2001
      From: DongCheol Kim 
      Date: Mon, 14 Oct 2024 15:15:17 +0900
      Subject: [PATCH 103/187] Improve `StreamMessageListenerContainer` Javadoc.
      
      Fix minor typos and improve the clarity of the StreamMessageListenerContainer Javadoc regarding subscription registration.
      
      Closes #3021
      ---
       .../StreamMessageListenerContainer.java       | 29 +++++++++++--------
       1 file changed, 17 insertions(+), 12 deletions(-)
      
      diff --git a/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java
      index ff06716ac6..febe221623 100644
      --- a/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java
      +++ b/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java
      @@ -106,6 +106,7 @@
        * @author Mark Paluch
        * @author Christoph Strobl
        * @author Christian Rest
      + * @author DongCheol Kim
        * @param  Stream key and Stream field type.
        * @param  Stream value type.
        * @since 2.2
      @@ -155,9 +156,10 @@ static StreamMessageListenerContainer>
       	}
       
       	/**
      -	 * Register a new subscription for a Redis Stream. If the {@link StreamMessageListenerContainer#isRunning() is already
      -	 * running} the {@link Subscription} will be added and run immediately, otherwise it'll be scheduled and started once
      -	 * the container is actually {@link StreamMessageListenerContainer#start() started}.
      +	 * Register a new subscription for a Redis Stream. If a {@link StreamMessageListenerContainer} is already
      +	 * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and run
      +	 * immediately, otherwise it'll be scheduled and started once the container is actually
      +	 * {@link StreamMessageListenerContainer#start() started}.
       	 * 

      * Errors during {@link Record} retrieval lead to {@link Subscription#cancel() cancellation} of the underlying task. *

      @@ -174,9 +176,10 @@ default Subscription receive(StreamOffset streamOffset, StreamListener } /** - * Register a new subscription for a Redis Stream. If the {@link StreamMessageListenerContainer#isRunning() is already - * running} the {@link Subscription} will be added and run immediately, otherwise it'll be scheduled and started once - * the container is actually {@link StreamMessageListenerContainer#start() started}. + * Register a new subscription for a Redis Stream. If a {@link StreamMessageListenerContainer} is already + * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and run + * immediately, otherwise it'll be scheduled and started once the container is actually + * {@link StreamMessageListenerContainer#start() started}. *

      * Every message must be acknowledged using * {@link org.springframework.data.redis.core.StreamOperations#acknowledge(Object, String, String...)} after @@ -200,9 +203,10 @@ default Subscription receive(Consumer consumer, StreamOffset streamOffset, St } /** - * Register a new subscription for a Redis Stream. If the {@link StreamMessageListenerContainer#isRunning() is already - * running} the {@link Subscription} will be added and run immediately, otherwise it'll be scheduled and started once - * the container is actually {@link StreamMessageListenerContainer#start() started}. + * Register a new subscription for a Redis Stream. If a {@link StreamMessageListenerContainer} is already + * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and run + * immediately, otherwise it'll be scheduled and started once the container is actually + * {@link StreamMessageListenerContainer#start() started}. *

      * Every message is acknowledged when received. *

      @@ -223,9 +227,10 @@ default Subscription receiveAutoAck(Consumer consumer, StreamOffset streamOff } /** - * Register a new subscription for a Redis Stream. If the {@link StreamMessageListenerContainer#isRunning() is already - * running} the {@link Subscription} will be added and run immediately, otherwise it'll be scheduled and started once - * the container is actually {@link StreamMessageListenerContainer#start() started}. + * Register a new subscription for a Redis Stream. If a {@link StreamMessageListenerContainer} is already + * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and run + * immediately, otherwise it'll be scheduled and started once the container is actually + * {@link StreamMessageListenerContainer#start() started}. *

      * Errors during {@link Record} are tested against test {@link StreamReadRequest#getCancelSubscriptionOnError() * cancellation predicate} whether to cancel the underlying task. From cb73f0b6ec9f222fdb57bd21a1b2611bf93b5368 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 14 Nov 2024 15:48:50 +0100 Subject: [PATCH 104/187] Polishing. Tweak wording. See #3021 --- .../StreamMessageListenerContainer.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java index febe221623..0d35c813fe 100644 --- a/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java @@ -156,8 +156,8 @@ static StreamMessageListenerContainer> } /** - * Register a new subscription for a Redis Stream. If a {@link StreamMessageListenerContainer} is already - * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and run + * Register a new subscription for a Redis Stream. If the container is already + * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and started * immediately, otherwise it'll be scheduled and started once the container is actually * {@link StreamMessageListenerContainer#start() started}. *

      @@ -176,8 +176,8 @@ default Subscription receive(StreamOffset streamOffset, StreamListener } /** - * Register a new subscription for a Redis Stream. If a {@link StreamMessageListenerContainer} is already - * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and run + * Register a new subscription for a Redis Stream. If the container is already + * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and started * immediately, otherwise it'll be scheduled and started once the container is actually * {@link StreamMessageListenerContainer#start() started}. *

      @@ -203,8 +203,8 @@ default Subscription receive(Consumer consumer, StreamOffset streamOffset, St } /** - * Register a new subscription for a Redis Stream. If a {@link StreamMessageListenerContainer} is already - * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and run + * Register a new subscription for a Redis Stream. If the container is already + * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and started * immediately, otherwise it'll be scheduled and started once the container is actually * {@link StreamMessageListenerContainer#start() started}. *

      @@ -227,8 +227,8 @@ default Subscription receiveAutoAck(Consumer consumer, StreamOffset streamOff } /** - * Register a new subscription for a Redis Stream. If a {@link StreamMessageListenerContainer} is already - * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and run + * Register a new subscription for a Redis Stream. If the container is already + * {@link StreamMessageListenerContainer#isRunning() running} the {@link Subscription} will be added and started * immediately, otherwise it'll be scheduled and started once the container is actually * {@link StreamMessageListenerContainer#start() started}. *

      @@ -250,9 +250,9 @@ default Subscription receiveAutoAck(Consumer consumer, StreamOffset streamOff /** * Unregister a given {@link Subscription} from the container. This prevents the {@link Subscription} to be restarted - * in a potential {@link SmartLifecycle#stop() stop}/{@link SmartLifecycle#start() start} scenario.
      - * An {@link Subscription#isActive() active} {@link Subscription subcription} is {@link Subscription#cancel() - * cancelled} prior to removal. + * in a potential {@link SmartLifecycle#stop() stop}/{@link SmartLifecycle#start() start} scenario. An + * {@link Subscription#isActive() active} {@link Subscription subcription} is {@link Subscription#cancel() cancelled} + * prior to removal. * * @param subscription must not be {@literal null}. */ From 4f32c69ebc1505abe2cfa540ee5118b4bfe0c6b9 Mon Sep 17 00:00:00 2001 From: Seungrae Kim Date: Thu, 10 Oct 2024 22:48:20 +0900 Subject: [PATCH 105/187] Fix typo in reference documentation. Closes #3018 --- src/main/antora/modules/ROOT/pages/redis/cluster.adoc | 6 +++--- src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/antora/modules/ROOT/pages/redis/cluster.adoc b/src/main/antora/modules/ROOT/pages/redis/cluster.adoc index 2dda2c0b94..e32c781614 100644 --- a/src/main/antora/modules/ROOT/pages/redis/cluster.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/cluster.adoc @@ -44,7 +44,7 @@ b8b5ee... 127.0.0.1:7382 slave 6b38bb... 0 1449730618304 25 connected < [source,java] ---- -RedisClusterConnection connection = connectionFactory.getClusterConnnection(); +RedisClusterConnection connection = connectionFactory.getClusterConnection(); connection.set("thing1", value); <5> connection.set("thing2", value); <6> @@ -88,7 +88,7 @@ redis-cli@127.0.0.1:7379 > cluster nodes [source,java] ---- -RedisClusterConnection connection = connectionFactory.getClusterConnnection(); +RedisClusterConnection connection = connectionFactory.getClusterConnection(); connection.set("thing1", value); // slot: 12182 connection.set("{thing1}.thing2", value); // slot: 12182 @@ -134,5 +134,5 @@ clusterOps.shutdown(NODE_7379); <1> <1> Shut down node at 7379 and cross fingers there is a replica in place that can take over. ==== -NOTE: Redis Cluster pipelining is currently only supported throug the Lettuce driver except for the following commands when using cross-slot keys: `rename`, `renameNX`, `sort`, `bLPop`, `bRPop`, `rPopLPush`, `bRPopLPush`, `info`, `sMove`, `sInter`, `sInterStore`, `sUnion`, `sUnionStore`, `sDiff`, `sDiffStore`. +NOTE: Redis Cluster pipelining is currently only supported through the Lettuce driver except for the following commands when using cross-slot keys: `rename`, `renameNX`, `sort`, `bLPop`, `bRPop`, `rPopLPush`, `bRPopLPush`, `info`, `sMove`, `sInter`, `sInterStore`, `sUnion`, `sUnionStore`, `sDiff`, `sDiffStore`. Same-slot keys are fully supported. diff --git a/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc b/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc index 3726bb0680..014aadb2e7 100644 --- a/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc @@ -47,7 +47,7 @@ It is possible to opt in to the locking behavior as follows: [source,java] ---- -RedisCacheManager cacheMangager = RedisCacheManager +RedisCacheManager cacheManager = RedisCacheManager .build(RedisCacheWriter.lockingRedisCacheWriter(connectionFactory)) .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()) ... From 82cb467bc4e05e83efd5f8af095c7346b7b838c3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 15 Nov 2024 14:10:37 +0100 Subject: [PATCH 106/187] Prepare 3.4 GA (2024.1.0). See #3025 --- pom.xml | 22 +++++----------------- src/main/resources/notice.txt | 3 ++- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 46cd6259e4..7083234984 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.4.0-SNAPSHOT + 3.4.0 - 3.4.0-SNAPSHOT - 3.4.0-SNAPSHOT + 3.4.0 + 3.4.0 4.0.2 1.9.4 1.4.21 @@ -389,19 +389,7 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - - - spring-milestone - https://repo.spring.io/milestone - + + diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 906935eb43..66c328a8a4 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 3.4 RC1 (2024.1.0) +Spring Data Redis 3.4 GA (2024.1.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -55,5 +55,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From d285626cba7b67517f3cfb442866902ad5aefed7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 15 Nov 2024 14:10:52 +0100 Subject: [PATCH 107/187] Release version 3.4 GA (2024.1.0). See #3025 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7083234984..a7a8d7ab3f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.4.0-SNAPSHOT + 3.4.0 Spring Data Redis Spring Data module for Redis From 6ba764ddd02ddd38680898a9a6543e20da60d465 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 15 Nov 2024 14:13:08 +0100 Subject: [PATCH 108/187] Prepare next development iteration. See #3025 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7a8d7ab3f..860b0c19d6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.4.0 + 3.5.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From b9755723a94f46290836853d913dcb01b3152262 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 15 Nov 2024 14:13:09 +0100 Subject: [PATCH 109/187] After release cleanups. See #3025 --- pom.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 860b0c19d6..50848f2fb4 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.4.0 + 3.5.0-SNAPSHOT - 3.4.0 - 3.4.0 + 3.5.0-SNAPSHOT + 3.5.0-SNAPSHOT 4.0.2 1.9.4 1.4.21 @@ -389,7 +389,19 @@ - - + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + + + spring-milestone + https://repo.spring.io/milestone + From 2b066a4acad70279102a8e27d6bf5677363ff8ae Mon Sep 17 00:00:00 2001 From: vaan_oxo <44669920+oxo1996@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:22:15 +0900 Subject: [PATCH 110/187] Fix incorrect reference to SETNX in ReactiveValueOperations.setIfAbsent Original pull request: #3047 Closes #3047 --- .../data/redis/core/ReactiveValueOperations.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java index d1ded81eae..92a8c65406 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java @@ -63,7 +63,7 @@ public interface ReactiveValueOperations { * * @param key must not be {@literal null}. * @param value - * @see Redis Documentation: SETNX + * @see Redis Documentation: SET */ Mono setIfAbsent(K key, V value); From 1ceb7ed5f1e77b2ecc5889b1fd16a91a3ca88e77 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 26 Nov 2024 11:29:44 +0100 Subject: [PATCH 111/187] Polishing. Updated more javadocs where reference to a Redis command was invalid. Original pull request: #3047 See: #3047 --- .../data/redis/core/ReactiveValueOperations.java | 4 +++- .../org/springframework/data/redis/core/ValueOperations.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java index 92a8c65406..ace20be127 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java @@ -54,7 +54,7 @@ public interface ReactiveValueOperations { * @param key must not be {@literal null}. * @param value * @param timeout must not be {@literal null}. - * @see Redis Documentation: SETEX + * @see Redis Documentation: SET */ Mono set(K key, V value, Duration timeout); @@ -281,6 +281,7 @@ public interface ReactiveValueOperations { * @param command must not be {@literal null}. * @return * @since 2.1 + * @see Redis Documentation: BITFIELD */ Mono> bitField(K key, BitFieldSubCommands command); @@ -288,6 +289,7 @@ public interface ReactiveValueOperations { * Removes the given {@literal key}. * * @param key must not be {@literal null}. + * @see Redis Documentation: DEL */ Mono delete(K key); } diff --git a/src/main/java/org/springframework/data/redis/core/ValueOperations.java b/src/main/java/org/springframework/data/redis/core/ValueOperations.java index 260bc7c4ca..7bdf6ad7c6 100644 --- a/src/main/java/org/springframework/data/redis/core/ValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ValueOperations.java @@ -62,7 +62,7 @@ public interface ValueOperations { * @param value must not be {@literal null}. * @param timeout must not be {@literal null}. * @throws IllegalArgumentException if either {@code key}, {@code value} or {@code timeout} is not present. - * @see Redis Documentation: SETEX + * @see Redis Documentation: SET * @since 2.1 */ default void set(K key, V value, Duration timeout) { @@ -82,7 +82,7 @@ default void set(K key, V value, Duration timeout) { * @param key must not be {@literal null}. * @param value must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. - * @see Redis Documentation: SETNX + * @see Redis Documentation: SET */ @Nullable Boolean setIfAbsent(K key, V value); From 3e16cc1bd58ee2f885b6eec8730672624adbede5 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 25 Nov 2024 14:12:37 +0100 Subject: [PATCH 112/187] Adds missing javadocs for `RedisOperations.hasKey()`. Also, added missing javadocs about what the method returns and about nullability. Closes #3049 Original pull request: #3055 --- .../springframework/data/redis/core/RedisOperations.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java index 0869015a8f..e3ca686a1e 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java @@ -47,6 +47,7 @@ * @author Todd Merrill * @author Chen Li * @author Vedran Pavic + * @author Marcin Grzejszczak */ public interface RedisOperations { @@ -151,7 +152,7 @@ T execute(RedisScript script, RedisSerializer argsSerializer, RedisSer * to free resources after use. * * @param callback must not be {@literal null}. - * @return + * @return the {@link Object result} of the operation performed in the callback or {@literal null}. * @since 1.8 */ @Nullable @@ -167,7 +168,7 @@ T execute(RedisScript script, RedisSerializer argsSerializer, RedisSer * @param sourceKey must not be {@literal null}. * @param targetKey must not be {@literal null}. * @param replace whether the key was copied. {@literal null} when used in pipeline / transaction. - * @return + * @return {@code true} when copied successfully or {@literal null} when used in pipeline / transaction. * @see Redis Documentation: COPY * @since 2.6 */ @@ -178,7 +179,7 @@ T execute(RedisScript script, RedisSerializer argsSerializer, RedisSer * Determine if given {@code key} exists. * * @param key must not be {@literal null}. - * @return + * @return {@literal true} if key exists. {@literal null} when used in pipeline / transaction. * @see Redis Documentation: EXISTS */ @Nullable @@ -361,7 +362,7 @@ default Boolean expireAt(K key, Instant expireAt) { * Remove the expiration from given {@code key}. * * @param key must not be {@literal null}. - * @return {@literal null} when used in pipeline / transaction. + * @return {@code true} when persisted successfully or {@literal null} when used in pipeline / transaction. * @see Redis Documentation: PERSIST */ @Nullable From 49f3f4e0d3ae4aa29a4cb4e45f535dcf6259a8a7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 28 Nov 2024 08:40:25 +0100 Subject: [PATCH 113/187] Polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tweak Javadoc wording for execute(…) with callback methods. See #3049 Original pull request: #3055 --- .../data/redis/core/RedisOperations.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java index e3ca686a1e..38d2a39c52 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java @@ -62,7 +62,7 @@ public interface RedisOperations { * * @param return type * @param action callback object that specifies the Redis action. Must not be {@literal null}. - * @return a result object returned by the action or {@literal null} + * @return result of the given {@link RedisCallback#doInRedis(RedisConnection)} invocation. */ @Nullable T execute(RedisCallback action); @@ -73,7 +73,7 @@ public interface RedisOperations { * * @param return type * @param session session callback. Must not be {@literal null}. - * @return result object returned by the action or {@literal null} + * @return result of the given {@link SessionCallback#execute(RedisOperations)} invocation. */ @Nullable T execute(SessionCallback session); @@ -84,7 +84,9 @@ public interface RedisOperations { * serializers to deserialize results * * @param action callback object to execute - * @return list of objects returned by the pipeline + * @return pipeline results of the given {@link RedisCallback#doInRedis(RedisConnection)} invocation. Results are + * collected from {@link RedisConnection} calls, {@link RedisCallback#doInRedis(RedisConnection)} itself must + * return {@literal null}. */ List executePipelined(RedisCallback action); @@ -95,7 +97,9 @@ public interface RedisOperations { * @param action callback object to execute * @param resultSerializer The Serializer to use for individual values or Collections of values. If any returned * values are hashes, this serializer will be used to deserialize both the key and value - * @return list of objects returned by the pipeline + * @return pipeline results of the given {@link RedisCallback#doInRedis(RedisConnection)} invocation. Results are + * collected from {@link RedisConnection} calls, {@link RedisCallback#doInRedis(RedisConnection)} itself must + * return {@literal null}. */ List executePipelined(RedisCallback action, RedisSerializer resultSerializer); @@ -104,7 +108,9 @@ public interface RedisOperations { * callback cannot return a non-null value as it gets overwritten by the pipeline. * * @param session Session callback - * @return list of objects returned by the pipeline + * @return pipeline results of the given {@link SessionCallback#execute(RedisOperations)} invocation. Results are + * collected from {@link RedisOperations} calls, {@link SessionCallback#execute(RedisOperations)} itself must + * return {@literal null}. */ List executePipelined(SessionCallback session); @@ -115,7 +121,9 @@ public interface RedisOperations { * * @param session Session callback * @param resultSerializer - * @return list of objects returned by the pipeline + * @return pipeline results of the given {@link SessionCallback#execute(RedisOperations)} invocation. Results are + * collected from {@link RedisOperations} calls, {@link SessionCallback#execute(RedisOperations)} itself must + * return {@literal null}. */ List executePipelined(SessionCallback session, RedisSerializer resultSerializer); From d338f2084367d80fa78ad235a953cbab64af8470 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 2 Dec 2024 08:11:31 +0100 Subject: [PATCH 114/187] Fix code example in readme. Closes #3060 --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index b62eb74359..d6e327b798 100644 --- a/README.adoc +++ b/README.adoc @@ -36,7 +36,7 @@ public class Example { // inject the actual template @Autowired - private RedisTemplate template; + private RedisTemplate redisTemplate; // inject the template as ListOperations // can also inject as Value, Set, ZSet, and HashOperations From 8579857cfcc9b4c92282a2049a8b559d37034120 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 4 Dec 2024 10:26:13 +0100 Subject: [PATCH 115/187] Polishing. Remove unused config files. See #3024 --- .gitignore | 1 + package.json | 10 ---------- 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 package.json diff --git a/.gitignore b/.gitignore index a0417bbada..62d57535ac 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ build/ node_modules node package-lock.json +package.json .mvn/.develocity diff --git a/package.json b/package.json deleted file mode 100644 index 4689506b3f..0000000000 --- a/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "dependencies": { - "antora": "3.2.0-alpha.6", - "@antora/atlas-extension": "1.0.0-alpha.2", - "@antora/collector-extension": "1.0.0-alpha.7", - "@asciidoctor/tabs": "1.0.0-beta.6", - "@springio/antora-extensions": "1.13.0", - "@springio/asciidoctor-extensions": "1.0.0-alpha.11" - } -} From c83f5ac56ffa3b65fcd5cc4ff9b3f83742c97c27 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 12 Dec 2024 09:10:41 +0100 Subject: [PATCH 116/187] Upgrade to Lettuce 6.5.1.RELEASE. Closes #3070 --- pom.xml | 2 +- .../data/redis/connection/lettuce/LettuceConnection.java | 2 +- .../observability/DefaultLettuceObservationConvention.java | 6 +++--- .../lettuce/LettuceConnectionFactoryUnitTests.java | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 50848f2fb4..fd73e23ebd 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 1.9.4 1.4.21 2.11.1 - 6.4.1.RELEASE + 6.5.1.RELEASE 5.2.0 1.01 4.1.115.Final diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java index aca9bb7a2b..bbf1755ab6 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java @@ -1075,7 +1075,7 @@ private void validateCommandIfRunningInTransactionMode(ProtocolKeyword cmd, byte private void validateCommand(ProtocolKeyword command, @Nullable byte[]... args) { - RedisCommand redisCommand = RedisCommand.failsafeCommandLookup(command.name()); + RedisCommand redisCommand = RedisCommand.failsafeCommandLookup(command.toString()); if (!RedisCommand.UNKNOWN.equals(redisCommand) && redisCommand.requiresArguments()) { try { diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java index 7d400eb9ba..1f21776ed2 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java @@ -42,7 +42,7 @@ public KeyValues getLowCardinalityKeyValues(LettuceObservationContext context) { Endpoint ep = context.getRequiredEndpoint(); KeyValues keyValues = KeyValues.of(LowCardinalityCommandKeyNames.DATABASE_SYSTEM.withValue("redis"), // - LowCardinalityCommandKeyNames.REDIS_COMMAND.withValue(context.getRequiredCommand().getType().name())); + LowCardinalityCommandKeyNames.REDIS_COMMAND.withValue(context.getRequiredCommand().getType().toString())); if (ep instanceof SocketAddressEndpoint endpoint) { @@ -70,7 +70,7 @@ public KeyValues getHighCardinalityKeyValues(LettuceObservationContext context) if (command.getArgs() != null) { return KeyValues.of(HighCardinalityCommandKeyNames.STATEMENT - .withValue(command.getType().name() + " " + command.getArgs().toCommandString())); + .withValue(command.getType().toString() + " " + command.getArgs().toCommandString())); } } @@ -79,6 +79,6 @@ public KeyValues getHighCardinalityKeyValues(LettuceObservationContext context) @Override public String getContextualName(LettuceObservationContext context) { - return context.getRequiredCommand().getType().name().toLowerCase(Locale.ROOT); + return context.getRequiredCommand().getType().toString().toLowerCase(Locale.ROOT); } } diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java index 548fce0f49..3242dacf73 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java @@ -1071,7 +1071,6 @@ void maxRedirectsShouldBeSetOnClusterClientOptions() { assertThat(options.getMaxRedirects()).isEqualTo(42); assertThat(options.isValidateClusterNodeMembership()).isFalse(); - assertThat(options.getTimeoutOptions().isApplyConnectionTimeout()).isFalse(); } @Test // DATAREDIS-1142 From 6966759c37b8ca04f5b57544fac3873e5bda05cf Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 8 Jan 2025 10:04:50 +0100 Subject: [PATCH 117/187] Extend license header copyright years to 2025. See #3088 --- src/main/antora/modules/ROOT/pages/redis/cluster.adoc | 4 ++-- .../modules/ROOT/pages/redis/redis-repositories/usage.adoc | 2 +- .../springframework/data/redis/ClusterRedirectException.java | 2 +- .../data/redis/ClusterStateFailureException.java | 2 +- .../data/redis/ExceptionTranslationStrategy.java | 2 +- .../data/redis/FallbackExceptionTranslationStrategy.java | 2 +- .../data/redis/PassThroughExceptionTranslationStrategy.java | 2 +- .../data/redis/RedisConnectionFailureException.java | 2 +- .../org/springframework/data/redis/RedisSystemException.java | 2 +- .../data/redis/TooManyClusterRedirectionsException.java | 2 +- .../org/springframework/data/redis/aot/RedisRuntimeHints.java | 2 +- .../org/springframework/data/redis/cache/BatchStrategies.java | 2 +- .../org/springframework/data/redis/cache/BatchStrategy.java | 2 +- .../org/springframework/data/redis/cache/CacheKeyPrefix.java | 2 +- .../org/springframework/data/redis/cache/CacheStatistics.java | 2 +- .../data/redis/cache/CacheStatisticsCollector.java | 2 +- .../data/redis/cache/CacheStatisticsProvider.java | 2 +- .../data/redis/cache/DefaultCacheStatisticsCollector.java | 2 +- .../data/redis/cache/DefaultRedisCacheWriter.java | 2 +- .../data/redis/cache/FixedDurationTtlFunction.java | 2 +- .../data/redis/cache/MutableCacheStatistics.java | 2 +- .../data/redis/cache/NoOpCacheStatisticsCollector.java | 2 +- .../java/org/springframework/data/redis/cache/RedisCache.java | 2 +- .../data/redis/cache/RedisCacheConfiguration.java | 2 +- .../springframework/data/redis/cache/RedisCacheManager.java | 2 +- .../springframework/data/redis/cache/RedisCacheWriter.java | 2 +- .../data/redis/config/RedisCollectionParser.java | 2 +- .../data/redis/config/RedisListenerContainerParser.java | 2 +- .../data/redis/config/RedisNamespaceHandler.java | 2 +- .../data/redis/connection/AbstractRedisConnection.java | 2 +- .../data/redis/connection/BitFieldSubCommands.java | 2 +- .../connection/ClusterCommandExecutionFailureException.java | 2 +- .../data/redis/connection/ClusterCommandExecutor.java | 2 +- .../springframework/data/redis/connection/ClusterInfo.java | 2 +- .../data/redis/connection/ClusterNodeResourceProvider.java | 2 +- .../data/redis/connection/ClusterSlotHashUtil.java | 2 +- .../data/redis/connection/ClusterTopology.java | 2 +- .../data/redis/connection/ClusterTopologyProvider.java | 2 +- .../data/redis/connection/ConnectionUtils.java | 2 +- .../org/springframework/data/redis/connection/DataType.java | 2 +- .../data/redis/connection/DecoratedRedisConnection.java | 2 +- .../springframework/data/redis/connection/DefaultMessage.java | 2 +- .../data/redis/connection/DefaultSortParameters.java | 2 +- .../data/redis/connection/DefaultStringRedisConnection.java | 2 +- .../data/redis/connection/DefaultStringTuple.java | 2 +- .../redis/connection/DefaultedRedisClusterConnection.java | 2 +- .../data/redis/connection/DefaultedRedisConnection.java | 2 +- .../springframework/data/redis/connection/FutureResult.java | 2 +- .../java/org/springframework/data/redis/connection/Limit.java | 2 +- .../org/springframework/data/redis/connection/Message.java | 2 +- .../data/redis/connection/MessageListener.java | 2 +- .../org/springframework/data/redis/connection/NamedNode.java | 2 +- .../springframework/data/redis/connection/PoolException.java | 2 +- .../data/redis/connection/ReactiveClusterCommands.java | 2 +- .../data/redis/connection/ReactiveClusterGeoCommands.java | 2 +- .../data/redis/connection/ReactiveClusterHashCommands.java | 2 +- .../redis/connection/ReactiveClusterHyperLogLogCommands.java | 2 +- .../data/redis/connection/ReactiveClusterKeyCommands.java | 2 +- .../data/redis/connection/ReactiveClusterListCommands.java | 2 +- .../data/redis/connection/ReactiveClusterNumberCommands.java | 2 +- .../redis/connection/ReactiveClusterScriptingCommands.java | 2 +- .../data/redis/connection/ReactiveClusterServerCommands.java | 2 +- .../data/redis/connection/ReactiveClusterSetCommands.java | 2 +- .../data/redis/connection/ReactiveClusterStreamCommands.java | 2 +- .../data/redis/connection/ReactiveClusterStringCommands.java | 2 +- .../data/redis/connection/ReactiveClusterZSetCommands.java | 2 +- .../data/redis/connection/ReactiveGeoCommands.java | 2 +- .../data/redis/connection/ReactiveHashCommands.java | 2 +- .../data/redis/connection/ReactiveHyperLogLogCommands.java | 2 +- .../data/redis/connection/ReactiveKeyCommands.java | 2 +- .../data/redis/connection/ReactiveListCommands.java | 2 +- .../data/redis/connection/ReactiveNumberCommands.java | 2 +- .../data/redis/connection/ReactivePubSubCommands.java | 2 +- .../data/redis/connection/ReactiveRedisClusterConnection.java | 2 +- .../data/redis/connection/ReactiveRedisConnection.java | 2 +- .../data/redis/connection/ReactiveRedisConnectionFactory.java | 2 +- .../data/redis/connection/ReactiveScriptingCommands.java | 2 +- .../data/redis/connection/ReactiveServerCommands.java | 2 +- .../data/redis/connection/ReactiveSetCommands.java | 2 +- .../data/redis/connection/ReactiveStreamCommands.java | 2 +- .../data/redis/connection/ReactiveStringCommands.java | 2 +- .../data/redis/connection/ReactiveSubscription.java | 2 +- .../data/redis/connection/ReactiveZSetCommands.java | 2 +- .../data/redis/connection/RedisClusterCommands.java | 2 +- .../data/redis/connection/RedisClusterCommandsProvider.java | 2 +- .../data/redis/connection/RedisClusterConfiguration.java | 2 +- .../data/redis/connection/RedisClusterConnection.java | 2 +- .../data/redis/connection/RedisClusterNode.java | 2 +- .../data/redis/connection/RedisClusterServerCommands.java | 2 +- .../springframework/data/redis/connection/RedisCommands.java | 2 +- .../data/redis/connection/RedisCommandsProvider.java | 2 +- .../data/redis/connection/RedisConfiguration.java | 2 +- .../data/redis/connection/RedisConnection.java | 2 +- .../data/redis/connection/RedisConnectionCommands.java | 2 +- .../data/redis/connection/RedisConnectionFactory.java | 2 +- .../data/redis/connection/RedisGeoCommands.java | 2 +- .../data/redis/connection/RedisHashCommands.java | 2 +- .../data/redis/connection/RedisHyperLogLogCommands.java | 2 +- .../redis/connection/RedisInvalidSubscriptionException.java | 2 +- .../data/redis/connection/RedisKeyCommands.java | 2 +- .../data/redis/connection/RedisListCommands.java | 2 +- .../org/springframework/data/redis/connection/RedisNode.java | 2 +- .../springframework/data/redis/connection/RedisPassword.java | 2 +- .../data/redis/connection/RedisPipelineException.java | 2 +- .../data/redis/connection/RedisPubSubCommands.java | 2 +- .../data/redis/connection/RedisScriptingCommands.java | 2 +- .../data/redis/connection/RedisSentinelCommands.java | 2 +- .../data/redis/connection/RedisSentinelConfiguration.java | 2 +- .../data/redis/connection/RedisSentinelConnection.java | 2 +- .../springframework/data/redis/connection/RedisServer.java | 2 +- .../data/redis/connection/RedisServerCommands.java | 2 +- .../data/redis/connection/RedisSetCommands.java | 2 +- .../data/redis/connection/RedisSocketConfiguration.java | 2 +- .../data/redis/connection/RedisStandaloneConfiguration.java | 2 +- .../connection/RedisStaticMasterReplicaConfiguration.java | 2 +- .../data/redis/connection/RedisStreamCommands.java | 2 +- .../data/redis/connection/RedisStringCommands.java | 2 +- .../redis/connection/RedisSubscribedConnectionException.java | 2 +- .../data/redis/connection/RedisTxCommands.java | 2 +- .../data/redis/connection/RedisZSetCommands.java | 2 +- .../org/springframework/data/redis/connection/ReturnType.java | 2 +- .../data/redis/connection/SentinelMasterId.java | 2 +- .../springframework/data/redis/connection/SortParameters.java | 2 +- .../data/redis/connection/StringRedisConnection.java | 2 +- .../springframework/data/redis/connection/Subscription.java | 2 +- .../data/redis/connection/SubscriptionListener.java | 2 +- .../springframework/data/redis/connection/ValueEncoding.java | 2 +- .../data/redis/connection/convert/Converters.java | 2 +- .../data/redis/connection/convert/ListConverter.java | 2 +- .../data/redis/connection/convert/LongToBooleanConverter.java | 2 +- .../data/redis/connection/convert/MapConverter.java | 2 +- .../redis/connection/convert/MapToPropertiesConverter.java | 2 +- .../data/redis/connection/convert/SetConverter.java | 2 +- .../redis/connection/convert/StringToDataTypeConverter.java | 2 +- .../redis/connection/convert/StringToPropertiesConverter.java | 2 +- .../connection/convert/StringToRedisClientInfoConverter.java | 2 +- .../redis/connection/convert/TransactionResultConverter.java | 2 +- .../connection/jedis/DefaultJedisClientConfiguration.java | 2 +- .../connection/jedis/JedisClientConfigBuilderCustomizer.java | 2 +- .../data/redis/connection/jedis/JedisClientConfiguration.java | 2 +- .../data/redis/connection/jedis/JedisClientUtils.java | 2 +- .../data/redis/connection/jedis/JedisClusterConnection.java | 2 +- .../data/redis/connection/jedis/JedisClusterGeoCommands.java | 2 +- .../data/redis/connection/jedis/JedisClusterHashCommands.java | 2 +- .../connection/jedis/JedisClusterHyperLogLogCommands.java | 2 +- .../data/redis/connection/jedis/JedisClusterKeyCommands.java | 2 +- .../data/redis/connection/jedis/JedisClusterListCommands.java | 2 +- .../redis/connection/jedis/JedisClusterScriptingCommands.java | 2 +- .../redis/connection/jedis/JedisClusterServerCommands.java | 2 +- .../data/redis/connection/jedis/JedisClusterSetCommands.java | 2 +- .../redis/connection/jedis/JedisClusterStreamCommands.java | 2 +- .../redis/connection/jedis/JedisClusterStringCommands.java | 2 +- .../data/redis/connection/jedis/JedisClusterZSetCommands.java | 2 +- .../data/redis/connection/jedis/JedisConnection.java | 2 +- .../data/redis/connection/jedis/JedisConnectionFactory.java | 2 +- .../data/redis/connection/jedis/JedisConverters.java | 2 +- .../data/redis/connection/jedis/JedisExceptionConverter.java | 2 +- .../data/redis/connection/jedis/JedisGeoCommands.java | 2 +- .../data/redis/connection/jedis/JedisHashCommands.java | 2 +- .../data/redis/connection/jedis/JedisHyperLogLogCommands.java | 2 +- .../data/redis/connection/jedis/JedisInvoker.java | 2 +- .../data/redis/connection/jedis/JedisKeyCommands.java | 2 +- .../data/redis/connection/jedis/JedisListCommands.java | 2 +- .../data/redis/connection/jedis/JedisMessageListener.java | 2 +- .../data/redis/connection/jedis/JedisResult.java | 2 +- .../redis/connection/jedis/JedisScriptReturnConverter.java | 2 +- .../data/redis/connection/jedis/JedisScriptingCommands.java | 2 +- .../data/redis/connection/jedis/JedisSentinelConnection.java | 2 +- .../data/redis/connection/jedis/JedisServerCommands.java | 2 +- .../data/redis/connection/jedis/JedisSetCommands.java | 2 +- .../data/redis/connection/jedis/JedisStreamCommands.java | 2 +- .../data/redis/connection/jedis/JedisStringCommands.java | 2 +- .../data/redis/connection/jedis/JedisSubscription.java | 2 +- .../data/redis/connection/jedis/JedisZSetCommands.java | 2 +- .../data/redis/connection/jedis/StreamConverters.java | 2 +- .../redis/connection/lettuce/ClusterConnectionProvider.java | 2 +- .../connection/lettuce/DefaultLettuceClientConfiguration.java | 2 +- .../lettuce/DefaultLettucePoolingClientConfiguration.java | 2 +- .../lettuce/LettuceByteBufferPubSubListenerWrapper.java | 2 +- .../redis/connection/lettuce/LettuceClientConfiguration.java | 2 +- .../redis/connection/lettuce/LettuceClusterConnection.java | 2 +- .../redis/connection/lettuce/LettuceClusterGeoCommands.java | 2 +- .../redis/connection/lettuce/LettuceClusterHashCommands.java | 2 +- .../connection/lettuce/LettuceClusterHyperLogLogCommands.java | 2 +- .../redis/connection/lettuce/LettuceClusterKeyCommands.java | 2 +- .../redis/connection/lettuce/LettuceClusterListCommands.java | 2 +- .../connection/lettuce/LettuceClusterServerCommands.java | 2 +- .../redis/connection/lettuce/LettuceClusterSetCommands.java | 2 +- .../connection/lettuce/LettuceClusterStringCommands.java | 2 +- .../connection/lettuce/LettuceClusterTopologyProvider.java | 2 +- .../redis/connection/lettuce/LettuceClusterZSetCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceConnection.java | 2 +- .../redis/connection/lettuce/LettuceConnectionFactory.java | 2 +- .../redis/connection/lettuce/LettuceConnectionProvider.java | 2 +- .../data/redis/connection/lettuce/LettuceConverters.java | 2 +- .../redis/connection/lettuce/LettuceExceptionConverter.java | 2 +- .../data/redis/connection/lettuce/LettuceFutureUtils.java | 2 +- .../data/redis/connection/lettuce/LettuceGeoCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceHashCommands.java | 2 +- .../redis/connection/lettuce/LettuceHyperLogLogCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceInvoker.java | 2 +- .../data/redis/connection/lettuce/LettuceKeyCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceListCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceMessageListener.java | 2 +- .../connection/lettuce/LettucePoolingClientConfiguration.java | 2 +- .../connection/lettuce/LettucePoolingConnectionProvider.java | 2 +- .../connection/lettuce/LettuceReactiveClusterGeoCommands.java | 2 +- .../lettuce/LettuceReactiveClusterHashCommands.java | 2 +- .../lettuce/LettuceReactiveClusterHyperLogLogCommands.java | 2 +- .../connection/lettuce/LettuceReactiveClusterKeyCommands.java | 2 +- .../lettuce/LettuceReactiveClusterListCommands.java | 2 +- .../lettuce/LettuceReactiveClusterNumberCommands.java | 2 +- .../lettuce/LettuceReactiveClusterScriptingCommands.java | 2 +- .../lettuce/LettuceReactiveClusterServerCommands.java | 2 +- .../connection/lettuce/LettuceReactiveClusterSetCommands.java | 2 +- .../lettuce/LettuceReactiveClusterStreamCommands.java | 2 +- .../lettuce/LettuceReactiveClusterStringCommands.java | 2 +- .../lettuce/LettuceReactiveClusterZSetCommands.java | 2 +- .../redis/connection/lettuce/LettuceReactiveGeoCommands.java | 2 +- .../redis/connection/lettuce/LettuceReactiveHashCommands.java | 2 +- .../lettuce/LettuceReactiveHyperLogLogCommands.java | 2 +- .../redis/connection/lettuce/LettuceReactiveKeyCommands.java | 2 +- .../redis/connection/lettuce/LettuceReactiveListCommands.java | 2 +- .../connection/lettuce/LettuceReactiveNumberCommands.java | 2 +- .../connection/lettuce/LettuceReactivePubSubCommands.java | 2 +- .../lettuce/LettuceReactiveRedisClusterConnection.java | 2 +- .../connection/lettuce/LettuceReactiveRedisConnection.java | 2 +- .../connection/lettuce/LettuceReactiveScriptingCommands.java | 2 +- .../connection/lettuce/LettuceReactiveServerCommands.java | 2 +- .../redis/connection/lettuce/LettuceReactiveSetCommands.java | 2 +- .../connection/lettuce/LettuceReactiveStreamCommands.java | 2 +- .../connection/lettuce/LettuceReactiveStringCommands.java | 2 +- .../redis/connection/lettuce/LettuceReactiveSubscription.java | 2 +- .../redis/connection/lettuce/LettuceReactiveZSetCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceResult.java | 2 +- .../data/redis/connection/lettuce/LettuceScanCursor.java | 2 +- .../redis/connection/lettuce/LettuceScriptingCommands.java | 2 +- .../redis/connection/lettuce/LettuceSentinelConnection.java | 2 +- .../data/redis/connection/lettuce/LettuceServerCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceSetCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceStreamCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceStringCommands.java | 2 +- .../data/redis/connection/lettuce/LettuceSubscription.java | 2 +- .../data/redis/connection/lettuce/LettuceZSetCommands.java | 2 +- .../data/redis/connection/lettuce/RangeConverter.java | 2 +- .../data/redis/connection/lettuce/RedisClientProvider.java | 2 +- .../connection/lettuce/RedisCredentialsProviderFactory.java | 2 +- .../connection/lettuce/StandaloneConnectionProvider.java | 2 +- .../lettuce/StaticMasterReplicaConnectionProvider.java | 2 +- .../data/redis/connection/lettuce/StreamConverters.java | 2 +- .../redis/connection/lettuce/aot/LettuceRuntimeHints.java | 2 +- .../observability/DefaultLettuceObservationConvention.java | 2 +- .../lettuce/observability/LettuceObservationContext.java | 2 +- .../lettuce/observability/LettuceObservationConvention.java | 2 +- .../lettuce/observability/MicrometerTracingAdapter.java | 2 +- .../connection/lettuce/observability/RedisObservation.java | 2 +- .../lettuce/observability/SocketAddressEndpoint.java | 2 +- .../data/redis/connection/stream/ByteBufferRecord.java | 2 +- .../data/redis/connection/stream/ByteRecord.java | 2 +- .../data/redis/connection/stream/Consumer.java | 2 +- .../data/redis/connection/stream/MapRecord.java | 2 +- .../data/redis/connection/stream/ObjectRecord.java | 2 +- .../data/redis/connection/stream/PendingMessage.java | 2 +- .../data/redis/connection/stream/PendingMessages.java | 2 +- .../data/redis/connection/stream/PendingMessagesSummary.java | 2 +- .../data/redis/connection/stream/ReadOffset.java | 2 +- .../springframework/data/redis/connection/stream/Record.java | 2 +- .../data/redis/connection/stream/RecordId.java | 2 +- .../data/redis/connection/stream/StreamInfo.java | 2 +- .../data/redis/connection/stream/StreamOffset.java | 2 +- .../data/redis/connection/stream/StreamReadOptions.java | 2 +- .../data/redis/connection/stream/StreamRecords.java | 2 +- .../data/redis/connection/stream/StreamSerialization.java | 2 +- .../data/redis/connection/stream/StringRecord.java | 2 +- .../data/redis/connection/util/AbstractSubscription.java | 2 +- .../data/redis/connection/util/ByteArraySet.java | 2 +- .../data/redis/connection/util/ByteArrayWrapper.java | 2 +- .../data/redis/connection/util/DecodeUtils.java | 2 +- .../springframework/data/redis/connection/zset/Aggregate.java | 2 +- .../data/redis/connection/zset/DefaultTuple.java | 2 +- .../org/springframework/data/redis/connection/zset/Tuple.java | 2 +- .../springframework/data/redis/connection/zset/Weights.java | 2 +- .../springframework/data/redis/core/AbstractOperations.java | 2 +- .../springframework/data/redis/core/BoundGeoOperations.java | 2 +- .../springframework/data/redis/core/BoundHashOperations.java | 2 +- .../springframework/data/redis/core/BoundKeyOperations.java | 2 +- .../springframework/data/redis/core/BoundListOperations.java | 2 +- .../data/redis/core/BoundOperationsProxyFactory.java | 2 +- .../springframework/data/redis/core/BoundSetOperations.java | 2 +- .../data/redis/core/BoundStreamOperations.java | 2 +- .../springframework/data/redis/core/BoundValueOperations.java | 2 +- .../springframework/data/redis/core/BoundZSetOperations.java | 2 +- .../java/org/springframework/data/redis/core/BulkMapper.java | 2 +- .../data/redis/core/CloseSuppressingInvocationHandler.java | 2 +- .../springframework/data/redis/core/ClusterOperations.java | 2 +- .../data/redis/core/ClusterOperationsEditor.java | 2 +- .../org/springframework/data/redis/core/ConvertingCursor.java | 2 +- src/main/java/org/springframework/data/redis/core/Cursor.java | 2 +- .../data/redis/core/DefaultClusterOperations.java | 2 +- .../springframework/data/redis/core/DefaultGeoOperations.java | 2 +- .../data/redis/core/DefaultHashOperations.java | 2 +- .../data/redis/core/DefaultHyperLogLogOperations.java | 2 +- .../data/redis/core/DefaultListOperations.java | 2 +- .../data/redis/core/DefaultReactiveGeoOperations.java | 2 +- .../data/redis/core/DefaultReactiveHashOperations.java | 2 +- .../data/redis/core/DefaultReactiveHyperLogLogOperations.java | 2 +- .../data/redis/core/DefaultReactiveListOperations.java | 2 +- .../data/redis/core/DefaultReactiveSetOperations.java | 2 +- .../data/redis/core/DefaultReactiveStreamOperations.java | 2 +- .../data/redis/core/DefaultReactiveValueOperations.java | 2 +- .../data/redis/core/DefaultReactiveZSetOperations.java | 2 +- .../springframework/data/redis/core/DefaultSetOperations.java | 2 +- .../data/redis/core/DefaultStreamOperations.java | 2 +- .../springframework/data/redis/core/DefaultTypedTuple.java | 2 +- .../data/redis/core/DefaultValueOperations.java | 2 +- .../data/redis/core/DefaultZSetOperations.java | 2 +- .../org/springframework/data/redis/core/GeoOperations.java | 2 +- .../springframework/data/redis/core/GeoOperationsEditor.java | 2 +- .../springframework/data/redis/core/HashMapperProvider.java | 2 +- .../org/springframework/data/redis/core/HashOperations.java | 2 +- .../springframework/data/redis/core/HashOperationsEditor.java | 2 +- .../data/redis/core/HyperLogLogOperations.java | 2 +- .../data/redis/core/HyperLogLogOperationsEditor.java | 2 +- .../java/org/springframework/data/redis/core/IndexWriter.java | 2 +- .../org/springframework/data/redis/core/KeyBoundCursor.java | 2 +- .../org/springframework/data/redis/core/KeyScanOptions.java | 2 +- .../org/springframework/data/redis/core/ListOperations.java | 2 +- .../springframework/data/redis/core/ListOperationsEditor.java | 2 +- .../org/springframework/data/redis/core/PartialUpdate.java | 2 +- .../data/redis/core/ReactiveGeoOperations.java | 2 +- .../data/redis/core/ReactiveHashOperations.java | 2 +- .../data/redis/core/ReactiveHyperLogLogOperations.java | 2 +- .../data/redis/core/ReactiveListOperations.java | 2 +- .../data/redis/core/ReactiveRedisCallback.java | 2 +- .../data/redis/core/ReactiveRedisOperations.java | 2 +- .../data/redis/core/ReactiveRedisSessionCallback.java | 2 +- .../data/redis/core/ReactiveRedisTemplate.java | 2 +- .../data/redis/core/ReactiveSetOperations.java | 2 +- .../data/redis/core/ReactiveStreamOperations.java | 2 +- .../data/redis/core/ReactiveStringRedisTemplate.java | 2 +- .../data/redis/core/ReactiveValueOperations.java | 2 +- .../data/redis/core/ReactiveZSetOperations.java | 2 +- .../org/springframework/data/redis/core/RedisAccessor.java | 2 +- .../org/springframework/data/redis/core/RedisCallback.java | 2 +- .../springframework/data/redis/core/RedisClusterCallback.java | 2 +- .../org/springframework/data/redis/core/RedisCommand.java | 2 +- .../springframework/data/redis/core/RedisConnectionUtils.java | 2 +- .../java/org/springframework/data/redis/core/RedisHash.java | 2 +- .../springframework/data/redis/core/RedisKeyExpiredEvent.java | 2 +- .../springframework/data/redis/core/RedisKeyValueAdapter.java | 2 +- .../data/redis/core/RedisKeyValueTemplate.java | 2 +- .../springframework/data/redis/core/RedisKeyspaceEvent.java | 2 +- .../org/springframework/data/redis/core/RedisOperations.java | 2 +- .../org/springframework/data/redis/core/RedisQueryEngine.java | 2 +- .../org/springframework/data/redis/core/RedisTemplate.java | 2 +- .../java/org/springframework/data/redis/core/ScanCursor.java | 2 +- .../org/springframework/data/redis/core/ScanIteration.java | 2 +- .../java/org/springframework/data/redis/core/ScanOptions.java | 2 +- .../org/springframework/data/redis/core/SessionCallback.java | 2 +- .../org/springframework/data/redis/core/SetOperations.java | 2 +- .../springframework/data/redis/core/SetOperationsEditor.java | 2 +- .../springframework/data/redis/core/StreamObjectMapper.java | 2 +- .../org/springframework/data/redis/core/StreamOperations.java | 2 +- .../data/redis/core/StreamOperationsEditor.java | 2 +- .../springframework/data/redis/core/StringRedisTemplate.java | 2 +- .../java/org/springframework/data/redis/core/TimeToLive.java | 2 +- .../springframework/data/redis/core/TimeToLiveAccessor.java | 2 +- .../org/springframework/data/redis/core/TimeoutUtils.java | 2 +- .../org/springframework/data/redis/core/ValueOperations.java | 2 +- .../data/redis/core/ValueOperationsEditor.java | 2 +- .../org/springframework/data/redis/core/ZSetOperations.java | 2 +- .../springframework/data/redis/core/ZSetOperationsEditor.java | 2 +- .../data/redis/core/convert/BinaryConverters.java | 2 +- .../org/springframework/data/redis/core/convert/Bucket.java | 2 +- .../data/redis/core/convert/CompositeIndexResolver.java | 2 +- .../data/redis/core/convert/DefaultRedisTypeMapper.java | 2 +- .../data/redis/core/convert/GeoIndexedPropertyValue.java | 2 +- .../data/redis/core/convert/IndexResolver.java | 2 +- .../springframework/data/redis/core/convert/IndexedData.java | 2 +- .../data/redis/core/convert/IndexedDataFactoryProvider.java | 2 +- .../data/redis/core/convert/Jsr310Converters.java | 2 +- .../data/redis/core/convert/KeyspaceConfiguration.java | 2 +- .../data/redis/core/convert/MappingConfiguration.java | 2 +- .../data/redis/core/convert/MappingRedisConverter.java | 2 +- .../data/redis/core/convert/PathIndexResolver.java | 2 +- .../data/redis/core/convert/RedisConverter.java | 2 +- .../data/redis/core/convert/RedisCustomConversions.java | 2 +- .../springframework/data/redis/core/convert/RedisData.java | 2 +- .../data/redis/core/convert/RedisTypeMapper.java | 2 +- .../data/redis/core/convert/ReferenceResolver.java | 2 +- .../data/redis/core/convert/ReferenceResolverImpl.java | 2 +- .../data/redis/core/convert/RemoveIndexedData.java | 2 +- .../data/redis/core/convert/SimpleIndexedPropertyValue.java | 2 +- .../data/redis/core/convert/SpelIndexResolver.java | 2 +- .../redis/core/index/ConfigurableIndexDefinitionProvider.java | 2 +- .../data/redis/core/index/GeoIndexDefinition.java | 2 +- .../org/springframework/data/redis/core/index/GeoIndexed.java | 2 +- .../data/redis/core/index/IndexConfiguration.java | 2 +- .../data/redis/core/index/IndexDefinition.java | 2 +- .../data/redis/core/index/IndexDefinitionProvider.java | 2 +- .../data/redis/core/index/IndexDefinitionRegistry.java | 2 +- .../data/redis/core/index/IndexValueTransformer.java | 2 +- .../org/springframework/data/redis/core/index/Indexed.java | 2 +- .../data/redis/core/index/PathBasedRedisIndexDefinition.java | 2 +- .../data/redis/core/index/RedisIndexDefinition.java | 2 +- .../data/redis/core/index/SimpleIndexDefinition.java | 2 +- .../data/redis/core/index/SpelIndexDefinition.java | 2 +- .../data/redis/core/mapping/BasicRedisPersistentEntity.java | 2 +- .../data/redis/core/mapping/RedisMappingContext.java | 2 +- .../data/redis/core/mapping/RedisPersistentEntity.java | 2 +- .../data/redis/core/mapping/RedisPersistentProperty.java | 2 +- .../data/redis/core/query/DefaultSortCriterion.java | 2 +- .../data/redis/core/query/DefaultSortQuery.java | 2 +- .../org/springframework/data/redis/core/query/QueryUtils.java | 2 +- .../springframework/data/redis/core/query/SortCriterion.java | 2 +- .../org/springframework/data/redis/core/query/SortQuery.java | 2 +- .../data/redis/core/query/SortQueryBuilder.java | 2 +- .../data/redis/core/script/DefaultReactiveScriptExecutor.java | 2 +- .../data/redis/core/script/DefaultRedisScript.java | 2 +- .../data/redis/core/script/DefaultScriptExecutor.java | 2 +- .../springframework/data/redis/core/script/DigestUtils.java | 2 +- .../data/redis/core/script/ReactiveScriptExecutor.java | 2 +- .../springframework/data/redis/core/script/RedisScript.java | 2 +- .../data/redis/core/script/ScriptExecutor.java | 2 +- .../springframework/data/redis/core/script/ScriptUtils.java | 2 +- .../data/redis/core/script/ScriptingException.java | 2 +- .../org/springframework/data/redis/core/types/Expiration.java | 2 +- .../data/redis/core/types/RedisClientInfo.java | 2 +- .../springframework/data/redis/domain/geo/BoundingBox.java | 2 +- .../org/springframework/data/redis/domain/geo/BoxShape.java | 2 +- .../springframework/data/redis/domain/geo/GeoLocation.java | 2 +- .../springframework/data/redis/domain/geo/GeoReference.java | 2 +- .../org/springframework/data/redis/domain/geo/GeoShape.java | 2 +- .../org/springframework/data/redis/domain/geo/Metrics.java | 2 +- .../springframework/data/redis/domain/geo/RadiusShape.java | 2 +- .../springframework/data/redis/hash/BeanUtilsHashMapper.java | 2 +- .../data/redis/hash/DecoratingStringHashMapper.java | 2 +- .../java/org/springframework/data/redis/hash/HashMapper.java | 2 +- .../springframework/data/redis/hash/Jackson2HashMapper.java | 2 +- .../org/springframework/data/redis/hash/ObjectHashMapper.java | 2 +- .../springframework/data/redis/listener/AbstractTopic.java | 2 +- .../org/springframework/data/redis/listener/ChannelTopic.java | 2 +- .../redis/listener/KeyExpirationEventMessageListener.java | 2 +- .../data/redis/listener/KeyspaceEventMessageListener.java | 2 +- .../org/springframework/data/redis/listener/PatternTopic.java | 2 +- .../redis/listener/ReactiveRedisMessageListenerContainer.java | 2 +- .../data/redis/listener/RedisMessageListenerContainer.java | 2 +- .../data/redis/listener/SynchronizingMessageListener.java | 2 +- .../java/org/springframework/data/redis/listener/Topic.java | 2 +- .../data/redis/listener/adapter/MessageListenerAdapter.java | 2 +- .../adapter/RedisListenerExecutionFailedException.java | 2 +- .../springframework/data/redis/repository/cdi/CdiBean.java | 2 +- .../data/redis/repository/cdi/RedisKeyValueAdapterBean.java | 2 +- .../data/redis/repository/cdi/RedisKeyValueTemplateBean.java | 2 +- .../data/redis/repository/cdi/RedisRepositoryBean.java | 2 +- .../data/redis/repository/cdi/RedisRepositoryExtension.java | 2 +- .../repository/configuration/EnableRedisRepositories.java | 2 +- .../repository/configuration/RedisRepositoriesRegistrar.java | 2 +- .../configuration/RedisRepositoryConfigurationExtension.java | 2 +- .../redis/repository/core/MappingRedisEntityInformation.java | 2 +- .../data/redis/repository/core/RedisEntityInformation.java | 2 +- .../data/redis/repository/query/ExampleQueryMapper.java | 2 +- .../data/redis/repository/query/RedisOperationChain.java | 2 +- .../data/redis/repository/query/RedisPartTreeQuery.java | 2 +- .../data/redis/repository/query/RedisQueryCreator.java | 2 +- .../redis/repository/support/QueryByExampleRedisExecutor.java | 2 +- .../data/redis/repository/support/RedisRepositoryFactory.java | 2 +- .../redis/repository/support/RedisRepositoryFactoryBean.java | 2 +- .../data/redis/serializer/ByteArrayRedisSerializer.java | 2 +- .../data/redis/serializer/DefaultRedisElementReader.java | 2 +- .../data/redis/serializer/DefaultRedisElementWriter.java | 2 +- .../redis/serializer/DefaultRedisSerializationContext.java | 2 +- .../data/redis/serializer/DefaultSerializationPair.java | 2 +- .../redis/serializer/GenericJackson2JsonRedisSerializer.java | 2 +- .../data/redis/serializer/GenericToStringSerializer.java | 2 +- .../data/redis/serializer/Jackson2JsonRedisSerializer.java | 2 +- .../data/redis/serializer/JacksonObjectReader.java | 2 +- .../data/redis/serializer/JacksonObjectWriter.java | 2 +- .../redis/serializer/JdkSerializationRedisSerializer.java | 2 +- .../springframework/data/redis/serializer/OxmSerializer.java | 2 +- .../data/redis/serializer/RedisElementReader.java | 2 +- .../data/redis/serializer/RedisElementWriter.java | 2 +- .../data/redis/serializer/RedisSerializationContext.java | 2 +- .../data/redis/serializer/RedisSerializer.java | 2 +- .../serializer/RedisSerializerToSerializationPairAdapter.java | 2 +- .../data/redis/serializer/SerializationException.java | 2 +- .../data/redis/serializer/SerializationUtils.java | 2 +- .../data/redis/serializer/StringRedisSerializer.java | 2 +- .../org/springframework/data/redis/stream/Cancelable.java | 2 +- .../redis/stream/DefaultStreamMessageListenerContainer.java | 2 +- .../data/redis/stream/DefaultStreamReceiver.java | 2 +- .../springframework/data/redis/stream/ReadOffsetStrategy.java | 2 +- .../org/springframework/data/redis/stream/StreamListener.java | 2 +- .../data/redis/stream/StreamMessageListenerContainer.java | 2 +- .../org/springframework/data/redis/stream/StreamPollTask.java | 2 +- .../org/springframework/data/redis/stream/StreamReceiver.java | 2 +- .../org/springframework/data/redis/stream/Subscription.java | 2 +- src/main/java/org/springframework/data/redis/stream/Task.java | 2 +- .../data/redis/support/atomic/CompareAndSet.java | 2 +- .../data/redis/support/atomic/RedisAtomicDouble.java | 2 +- .../data/redis/support/atomic/RedisAtomicInteger.java | 2 +- .../data/redis/support/atomic/RedisAtomicLong.java | 2 +- .../redis/support/collections/AbstractRedisCollection.java | 2 +- .../data/redis/support/collections/CollectionUtils.java | 2 +- .../data/redis/support/collections/DefaultRedisList.java | 2 +- .../data/redis/support/collections/DefaultRedisMap.java | 2 +- .../data/redis/support/collections/DefaultRedisSet.java | 2 +- .../data/redis/support/collections/DefaultRedisZSet.java | 2 +- .../data/redis/support/collections/RedisCollection.java | 2 +- .../redis/support/collections/RedisCollectionFactoryBean.java | 2 +- .../data/redis/support/collections/RedisIterator.java | 2 +- .../data/redis/support/collections/RedisList.java | 2 +- .../data/redis/support/collections/RedisMap.java | 2 +- .../data/redis/support/collections/RedisProperties.java | 2 +- .../data/redis/support/collections/RedisSet.java | 2 +- .../data/redis/support/collections/RedisStore.java | 2 +- .../data/redis/support/collections/RedisZSet.java | 2 +- .../data/redis/support/collections/ReversedRedisListView.java | 2 +- .../java/org/springframework/data/redis/util/ByteUtils.java | 2 +- .../org/springframework/data/redis/util/RedisAssertions.java | 2 +- .../data/redis/core/PartialUpdateExtensions.kt | 2 +- .../data/redis/core/ReactiveGeoOperationsExtensions.kt | 2 +- .../data/redis/core/ReactiveHashOperationsExtensions.kt | 2 +- .../redis/core/ReactiveHyperLogLogOperationsExtensions.kt | 2 +- .../data/redis/core/ReactiveListOperationsExtensions.kt | 2 +- .../data/redis/core/ReactiveRedisOperationsExtensions.kt | 2 +- .../data/redis/core/ReactiveSetOperationsExtensions.kt | 2 +- .../data/redis/core/ReactiveStreamOperationsExtensions.kt | 2 +- .../data/redis/core/ReactiveValueOperationsExtensions.kt | 2 +- .../data/redis/core/ReactiveZSetOperationsExtensions.kt | 2 +- .../data/redis/core/script/RedisScriptExtensions.kt | 2 +- src/test/java/org/springframework/data/redis/Address.java | 2 +- .../springframework/data/redis/ByteBufferObjectFactory.java | 2 +- .../springframework/data/redis/ConnectionFactoryTracker.java | 2 +- .../data/redis/DoubleAsStringObjectFactory.java | 2 +- .../org/springframework/data/redis/DoubleObjectFactory.java | 2 +- .../springframework/data/redis/LongAsStringObjectFactory.java | 2 +- .../org/springframework/data/redis/LongObjectFactory.java | 2 +- .../java/org/springframework/data/redis/ObjectFactory.java | 2 +- src/test/java/org/springframework/data/redis/Person.java | 2 +- .../org/springframework/data/redis/PersonObjectFactory.java | 2 +- .../springframework/data/redis/PrefixStringObjectFactory.java | 2 +- .../data/redis/PropertyEditorsIntegrationTests.java | 2 +- .../java/org/springframework/data/redis/RawObjectFactory.java | 2 +- src/test/java/org/springframework/data/redis/RedisViewPE.java | 2 +- .../java/org/springframework/data/redis/SettingsUtils.java | 2 +- .../org/springframework/data/redis/StringObjectFactory.java | 2 +- .../java/org/springframework/data/redis/TestCondition.java | 2 +- .../org/springframework/data/redis/cache/CacheTestParams.java | 2 +- .../redis/cache/DefaultCacheStatisticsCollectorUnitTests.java | 2 +- .../data/redis/cache/DefaultRedisCachWriterUnitTests.java | 2 +- .../data/redis/cache/DefaultRedisCacheWriterTests.java | 2 +- .../data/redis/cache/LegacyRedisCacheTests.java | 2 +- .../data/redis/cache/MutableCacheStatisticsUnitTests.java | 2 +- .../data/redis/cache/RedisCacheConfigurationUnitTests.java | 2 +- .../data/redis/cache/RedisCacheManagerUnitTests.java | 2 +- .../org/springframework/data/redis/cache/RedisCacheTests.java | 2 +- .../springframework/data/redis/cache/RedisCacheUnitTests.java | 2 +- .../data/redis/cache/RedisCacheWriterUnitTests.java | 2 +- .../data/redis/config/NamespaceIntegrationTests.java | 2 +- .../redis/config/PropertyEditorSupportIntegrationTests.java | 2 +- .../springframework/data/redis/config/StubErrorHandler.java | 2 +- .../redis/connection/AbstractConnectionIntegrationTests.java | 2 +- .../AbstractConnectionPipelineIntegrationTests.java | 2 +- .../AbstractConnectionTransactionIntegrationTests.java | 2 +- .../data/redis/connection/AbstractConnectionUnitTestBase.java | 2 +- .../data/redis/connection/AbstractTransactionalTestBase.java | 2 +- .../data/redis/connection/BitFieldSubCommandsUnitTests.java | 2 +- .../redis/connection/ClusterCommandExecutorUnitTests.java | 2 +- .../data/redis/connection/ClusterConnectionTests.java | 2 +- .../data/redis/connection/ClusterSlotHashUtilsTests.java | 2 +- .../data/redis/connection/ClusterTestVariables.java | 2 +- .../connection/DefaultStringRedisConnectionPipelineTests.java | 2 +- .../DefaultStringRedisConnectionPipelineTxTests.java | 2 +- .../redis/connection/DefaultStringRedisConnectionTests.java | 2 +- .../redis/connection/DefaultStringRedisConnectionTxTests.java | 2 +- .../redis/connection/ReactiveStreamCommandsUnitTests.java | 2 +- .../redis/connection/RedisClusterConfigurationUnitTests.java | 2 +- .../redis/connection/RedisClusterNodeSlotRangeUnitTests.java | 2 +- .../data/redis/connection/RedisConnectionUnitTests.java | 2 +- .../connection/RedisElastiCacheConfigurationUnitTests.java | 2 +- .../data/redis/connection/RedisNodeUnitTests.java | 2 +- .../data/redis/connection/RedisPasswordUnitTests.java | 2 +- .../redis/connection/RedisSentinelConfigurationUnitTests.java | 2 +- .../data/redis/connection/RedisServerUnitTests.java | 2 +- .../data/redis/connection/RedisStreamCommandsUnitTests.java | 2 +- .../data/redis/connection/RedisZSetCommandsUnitTests.java | 2 +- .../data/redis/connection/ReturnTypeUnitTests.java | 2 +- .../data/redis/connection/StreamRecordsUnitTests.java | 2 +- .../data/redis/connection/WeightsUnitTests.java | 2 +- .../data/redis/connection/convert/ConvertersUnitTests.java | 2 +- .../data/redis/connection/jedis/JedisAclIntegrationTests.java | 2 +- .../connection/jedis/JedisClientConfigurationUnitTests.java | 2 +- .../redis/connection/jedis/JedisClusterConnectionTests.java | 2 +- .../jedis/JedisConnectionFactoryIntegrationTests.java | 2 +- .../jedis/JedisConnectionFactorySentinelIntegrationTests.java | 2 +- .../connection/jedis/JedisConnectionFactoryUnitTests.java | 2 +- .../connection/jedis/JedisConnectionIntegrationTests.java | 2 +- .../jedis/JedisConnectionPipelineIntegrationTests.java | 2 +- .../jedis/JedisConnectionTransactionIntegrationTests.java | 2 +- .../data/redis/connection/jedis/JedisConnectionUnitTests.java | 2 +- .../data/redis/connection/jedis/JedisConvertersUnitTests.java | 2 +- .../connection/jedis/JedisExceptionConverterUnitTests.java | 2 +- .../connection/jedis/JedisSentinelConnectionUnitTests.java | 2 +- .../redis/connection/jedis/JedisSentinelIntegrationTests.java | 2 +- .../redis/connection/jedis/JedisSubscriptionUnitTests.java | 2 +- .../jedis/JedisTransactionalConnectionStarvationTest.java | 2 +- .../data/redis/connection/jedis/ScanTests.java | 2 +- .../connection/jedis/TransactionalJedisIntegrationTests.java | 2 +- .../jedis/extension/JedisConnectionFactoryExtension.java | 2 +- .../redis/connection/lettuce/LettuceAclIntegrationTests.java | 2 +- .../lettuce/LettuceClientConfigurationUnitTests.java | 2 +- .../connection/lettuce/LettuceClusterConnectionTests.java | 2 +- .../connection/lettuce/LettuceClusterConnectionUnitTests.java | 2 +- .../lettuce/LettuceClusterKeyspaceNotificationsTests.java | 2 +- .../connection/lettuce/LettuceCommandArgsComparator.java | 2 +- .../connection/lettuce/LettuceConnectionFactoryTests.java | 2 +- .../connection/lettuce/LettuceConnectionFactoryUnitTests.java | 2 +- .../connection/lettuce/LettuceConnectionIntegrationTests.java | 2 +- .../LettuceConnectionPipelineFlushOnEndIntegrationTests.java | 2 +- .../lettuce/LettuceConnectionPipelineIntegrationTests.java | 2 +- ...LettuceConnectionPipelineTxFlushOnEndIntegrationTests.java | 2 +- .../lettuce/LettuceConnectionPipelineTxIntegrationTests.java | 2 +- .../lettuce/LettuceConnectionTransactionIntegrationTests.java | 2 +- .../redis/connection/lettuce/LettuceConnectionUnitTests.java | 2 +- .../redis/connection/lettuce/LettuceConvertersUnitTests.java | 2 +- .../lettuce/LettucePoolingClientConfigurationUnitTests.java | 2 +- .../lettuce/LettucePoolingConnectionProviderUnitTests.java | 2 +- .../LettuceReactiveClusterCommandsIntegrationTests.java | 2 +- ...uceReactiveClusterHyperLogLogCommandsIntegrationTests.java | 2 +- .../LettuceReactiveClusterKeyCommandsIntegrationTests.java | 2 +- .../LettuceReactiveClusterListCommandsIntegrationTests.java | 2 +- .../LettuceReactiveClusterServerCommandsIntegrationTests.java | 2 +- .../LettuceReactiveClusterStringCommandsIntegrationTests.java | 2 +- .../connection/lettuce/LettuceReactiveClusterTestSupport.java | 2 +- .../LettuceReactiveClusterZSetCommandsIntegrationTests.java | 2 +- .../lettuce/LettuceReactiveCommandsTestSupport.java | 2 +- .../lettuce/LettuceReactiveGeoCommandsIntegrationTests.java | 2 +- .../lettuce/LettuceReactiveHashCommandsIntegrationTests.java | 2 +- .../lettuce/LettuceReactiveHyperLogLogCommandsTests.java | 2 +- .../lettuce/LettuceReactiveKeyCommandsIntegrationTests.java | 2 +- .../lettuce/LettuceReactiveListCommandIntegrationTests.java | 2 +- .../LettuceReactiveNumberCommandsIntegrationTests.java | 2 +- .../lettuce/LettuceReactivePubSubCommandsUnitTests.java | 2 +- .../LettuceReactiveRedisClusterConnectionUnitTests.java | 2 +- .../lettuce/LettuceReactiveRedisConnectionUnitTests.java | 2 +- .../LettuceReactiveScriptingCommandsIntegrationTests.java | 2 +- .../LettuceReactiveServerCommandsIntegrationTests.java | 2 +- ...LettuceReactiveSetCommandsIntegrationIntegrationTests.java | 2 +- .../LettuceReactiveStreamCommandsIntegrationTests.java | 2 +- .../LettuceReactiveStringCommandsIntegrationTests.java | 2 +- .../lettuce/LettuceReactiveSubscriptionUnitTests.java | 2 +- .../lettuce/LettuceReactiveZSetCommandsIntegrationTests.java | 2 +- .../lettuce/LettuceSentinelConnectionUnitTests.java | 2 +- .../connection/lettuce/LettuceSentinelIntegrationTests.java | 2 +- .../connection/lettuce/LettuceSubscriptionUnitTests.java | 2 +- .../connection/lettuce/LettuceTestClientConfiguration.java | 2 +- .../connection/lettuce/PipeliningFlushPolicyUnitTests.java | 2 +- .../StaticMasterReplicaConnectionProviderIntegrationTest.java | 2 +- .../lettuce/TransactionalLettuceIntegrationTests.java | 2 +- .../lettuce/extension/LettuceConnectionFactoryExtension.java | 2 +- .../lettuce/observability/ReactiveIntegrationTests.java | 2 +- .../lettuce/observability/SynchronousIntegrationTests.java | 2 +- .../redis/connection/lettuce/observability/TestConfig.java | 2 +- .../redis/connection/stream/StreamReadOptionsUnitTests.java | 2 +- .../data/redis/core/AbstractOperationsTestParams.java | 2 +- .../core/BoundOperationsProxyFactoryRuntimeHintTests.java | 2 +- .../data/redis/core/BoundOperationsProxyFactoryUnitTests.java | 2 +- .../data/redis/core/ConnectionMockingRedisTemplate.java | 2 +- .../redis/core/ConnectionSplittingInterceptorUnitTests.java | 2 +- .../data/redis/core/ConvertingCursorUnitTests.java | 2 +- .../data/redis/core/DefaultClusterOperationsUnitTests.java | 2 +- .../data/redis/core/DefaultGeoOperationsIntegrationTests.java | 2 +- .../redis/core/DefaultHashOperationsIntegrationTests.java | 2 +- .../core/DefaultHyperLogLogOperationsIntegrationTests.java | 2 +- .../DefaultListOperationsIntegrationIntegrationTests.java | 2 +- .../core/DefaultReactiveGeoOperationsIntegrationTests.java | 2 +- .../core/DefaultReactiveHashOperationsIntegrationTests.java | 2 +- .../DefaultReactiveHyperLogLogOperationsIntegrationTests.java | 2 +- .../core/DefaultReactiveListOperationsIntegrationTests.java | 2 +- .../core/DefaultReactiveSetOperationsIntegrationTests.java | 2 +- .../core/DefaultReactiveStreamOperationsIntegrationTests.java | 2 +- .../core/DefaultReactiveValueOperationsIntegrationTests.java | 2 +- .../core/DefaultReactiveZSetOperationsIntegrationTests.java | 2 +- .../data/redis/core/DefaultSetOperationsIntegrationTests.java | 2 +- .../redis/core/DefaultStreamOperationsIntegrationTests.java | 2 +- .../data/redis/core/DefaultTypedTupleUnitTests.java | 2 +- .../redis/core/DefaultValueOperationsIntegrationTests.java | 2 +- .../data/redis/core/DefaultValueOperationsUnitTests.java | 2 +- .../redis/core/DefaultZSetOperationsIntegrationTests.java | 2 +- .../data/redis/core/DefaultZSetOperationsUnitTests.java | 2 +- .../springframework/data/redis/core/IndexWriterUnitTests.java | 2 +- .../data/redis/core/MappingExpirationListenerTest.java | 2 +- .../core/MultithreadedRedisTemplateIntegrationTests.java | 2 +- .../data/redis/core/ReactiveOperationsTestParams.java | 2 +- .../redis/core/ReactiveRedisTemplateIntegrationTests.java | 2 +- .../data/redis/core/ReactiveRedisTemplateUnitTests.java | 2 +- .../core/ReactiveStringRedisTemplateIntegrationTests.java | 2 +- .../data/redis/core/RedisAccessorUnitTests.java | 2 +- .../data/redis/core/RedisClusterTemplateIntegrationTests.java | 2 +- .../data/redis/core/RedisCommandUnitTests.java | 2 +- .../data/redis/core/RedisConnectionUtilsUnitTests.java | 2 +- .../data/redis/core/RedisKeyExpiredEventUnitTests.java | 2 +- .../data/redis/core/RedisKeyValueAdapterTests.java | 2 +- .../data/redis/core/RedisKeyValueAdapterUnitTests.java | 2 +- .../data/redis/core/RedisKeyValueTemplateTests.java | 2 +- .../data/redis/core/RedisTemplateIntegrationTests.java | 2 +- .../data/redis/core/RedisTemplateUnitTests.java | 2 +- .../springframework/data/redis/core/ScanCursorUnitTests.java | 2 +- .../org/springframework/data/redis/core/SessionUnitTests.java | 2 +- .../data/redis/core/TimeoutUtilsUnitTests.java | 2 +- .../redis/core/convert/BinaryKeyspaceIdentifierUnitTests.java | 2 +- .../redis/core/convert/CompositeIndexResolverUnitTests.java | 2 +- .../data/redis/core/convert/ConversionTestEntities.java | 2 +- .../redis/core/convert/DefaultRedisTypeMapperUnitTests.java | 2 +- .../data/redis/core/convert/Jsr310ConvertersTest.java | 2 +- .../data/redis/core/convert/KeyspaceIdentifierUnitTests.java | 2 +- .../redis/core/convert/MappingRedisConverterUnitTests.java | 2 +- .../data/redis/core/convert/PathIndexResolverUnitTests.java | 2 +- .../data/redis/core/convert/SpelIndexResolverUnitTests.java | 2 +- .../data/redis/core/index/IndexConfigurationUnitTests.java | 2 +- .../core/mapping/BasicRedisPersistentEntityUnitTests.java | 2 +- .../core/mapping/ConfigAwareKeySpaceResolverUnitTests.java | 2 +- .../core/mapping/ConfigAwareTimeToLiveAccessorUnitTests.java | 2 +- .../redis/core/script/AbstractDefaultScriptExecutorTests.java | 2 +- .../redis/core/script/DefaultReactiveScriptExecutorTests.java | 2 +- .../core/script/DefaultReactiveScriptExecutorUnitTests.java | 2 +- .../data/redis/core/script/DefaultRedisScriptTests.java | 2 +- .../redis/core/script/DefaultScriptExecutorUnitTests.java | 2 +- .../core/script/jedis/JedisDefaultScriptExecutorTests.java | 2 +- .../script/lettuce/LettuceDefaultScriptExecutorTests.java | 2 +- .../data/redis/core/types/ExpirationUnitTests.java | 2 +- .../data/redis/core/types/RedisClientInfoUnitTests.java | 2 +- .../data/redis/domain/geo/BoundingBoxUnitTests.java | 2 +- .../data/redis/examples/ReactiveRedisApplication.java | 2 +- .../springframework/data/redis/examples/RedisApplication.java | 2 +- .../KeyExpirationEventMessageListenerIntegrationTests.java | 2 +- .../listener/KeyExpirationEventMessageListenerUnitTests.java | 2 +- .../springframework/data/redis/listener/PubSubAwaitUtil.java | 2 +- .../data/redis/listener/PubSubResubscribeTests.java | 2 +- .../springframework/data/redis/listener/PubSubTestParams.java | 2 +- .../org/springframework/data/redis/listener/PubSubTests.java | 2 +- .../data/redis/listener/ReactiveOperationsTestParams.java | 2 +- ...ReactiveRedisMessageListenerContainerIntegrationTests.java | 2 +- .../ReactiveRedisMessageListenerContainerUnitTests.java | 2 +- .../RedisMessageListenerContainerFailureIntegrationTests.java | 2 +- .../RedisMessageListenerContainerIntegrationTests.java | 2 +- .../listener/RedisMessageListenerContainerUnitTests.java | 2 +- .../data/redis/listener/SubscriptionConnectionTests.java | 2 +- .../listener/adapter/ContainerXmlSetupIntegrationTests.java | 2 +- .../data/redis/listener/adapter/MessageListenerUnitTests.java | 2 +- .../springframework/data/redis/listener/adapter/RedisMDP.java | 2 +- .../data/redis/listener/adapter/ThrowableMessageListener.java | 2 +- .../data/redis/mapping/AbstractHashMapperTests.java | 2 +- .../data/redis/mapping/BeanUtilsHashMapperTests.java | 2 +- .../redis/mapping/Jackson2HashMapperFlatteningUnitTests.java | 2 +- .../redis/mapping/Jackson2HashMapperIntegrationTests.java | 2 +- .../mapping/Jackson2HashMapperNonFlatteningUnitTests.java | 2 +- .../data/redis/mapping/Jackson2HashMapperUnitTests.java | 2 +- .../data/redis/mapping/ObjectHashMapperTests.java | 2 +- .../repository/RedisRepositoryClusterIntegrationTests.java | 2 +- .../redis/repository/RedisRepositoryIntegrationTestBase.java | 2 +- .../redis/repository/RedisRepositoryIntegrationTests.java | 2 +- .../redis/repository/cdi/CdiExtensionIntegrationTests.java | 2 +- .../org/springframework/data/redis/repository/cdi/Person.java | 2 +- .../springframework/data/redis/repository/cdi/PersonDB.java | 2 +- .../data/redis/repository/cdi/PersonFragment.java | 2 +- .../data/redis/repository/cdi/PersonFragmentImpl.java | 2 +- .../data/redis/repository/cdi/PersonRepository.java | 2 +- .../data/redis/repository/cdi/QualifiedPersonRepository.java | 2 +- .../redis/repository/cdi/RedisCdiDependenciesProducer.java | 2 +- .../data/redis/repository/cdi/RepositoryConsumer.java | 2 +- .../configuration/RedisRepositoriesRegistrarUnitTests.java | 2 +- .../RedisRepositoryConfigurationExtensionUnitTests.java | 2 +- .../configuration/RedisRepositoryConfigurationUnitTests.java | 2 +- .../core/MappingRedisEntityInformationUnitTests.java | 2 +- .../redis/repository/query/ExampleQueryMapperUnitTests.java | 2 +- .../redis/repository/query/RedisQueryCreatorUnitTests.java | 2 +- .../support/QueryByExampleRedisExecutorIntegrationTests.java | 2 +- .../redis/serializer/DefaultRedisElementReaderUnitTests.java | 2 +- .../redis/serializer/DefaultRedisElementWriterUnitTests.java | 2 +- .../GenericJackson2JsonRedisSerializerUnitTests.java | 2 +- .../redis/serializer/Jackson2JsonRedisSerializerTests.java | 2 +- .../redis/serializer/RedisSerializationContextUnitTests.java | 2 +- .../data/redis/serializer/SerializableDomainClass.java | 2 +- .../data/redis/serializer/SimpleRedisSerializerTests.java | 2 +- .../data/redis/serializer/StringRedisSerializerUnitTests.java | 2 +- ...bstractStreamMessageListenerContainerIntegrationTests.java | 2 +- .../JedisStreamMessageListenerContainerIntegrationTests.java | 2 +- ...LettuceStreamMessageListenerContainerIntegrationTests.java | 2 +- .../data/redis/stream/ReadOffsetStrategyUnitTests.java | 2 +- .../data/redis/stream/StreamReceiverIntegrationTests.java | 2 +- .../redis/support/BoundKeyOperationsIntegrationTests.java | 2 +- .../springframework/data/redis/support/BoundKeyParams.java | 2 +- .../data/redis/support/atomic/AtomicCountersParam.java | 2 +- .../atomic/CompareAndSetIntegrationIntegrationTests.java | 2 +- .../support/atomic/RedisAtomicDoubleIntegrationTests.java | 2 +- .../data/redis/support/atomic/RedisAtomicDoubleUnitTests.java | 2 +- .../support/atomic/RedisAtomicIntegerIntegrationTests.java | 2 +- .../redis/support/atomic/RedisAtomicIntegerUnitTests.java | 2 +- .../redis/support/atomic/RedisAtomicLongIntegrationTests.java | 2 +- .../data/redis/support/atomic/RedisAtomicLongUnitTests.java | 2 +- .../collections/AbstractRedisCollectionIntegrationTests.java | 2 +- .../support/collections/AbstractRedisCollectionUnitTests.java | 2 +- .../collections/AbstractRedisListIntegrationTests.java | 2 +- .../support/collections/AbstractRedisMapIntegrationTests.java | 2 +- .../support/collections/AbstractRedisSetIntegrationTests.java | 2 +- .../support/collections/AbstractRedisZSetTestIntegration.java | 2 +- .../data/redis/support/collections/CollectionTestParams.java | 2 +- .../support/collections/DefaultRedisMapUnitUnitTests.java | 2 +- .../support/collections/RedisCollectionFactoryBeanTests.java | 2 +- .../redis/support/collections/RedisListIntegrationTests.java | 2 +- .../redis/support/collections/RedisMapIntegrationTests.java | 2 +- .../support/collections/RedisPropertiesIntegrationTests.java | 2 +- .../redis/support/collections/RedisSetIntegrationTests.java | 2 +- .../redis/support/collections/RedisZSetIntegrationTests.java | 2 +- .../redis/support/collections/SupportXmlIntegrationTests.java | 2 +- .../data/redis/test/XstreamOxmSerializerSingleton.java | 2 +- .../data/redis/test/condition/EnabledIfLongRunningTest.java | 2 +- .../data/redis/test/condition/EnabledOnCommand.java | 2 +- .../data/redis/test/condition/EnabledOnCommandCondition.java | 2 +- .../data/redis/test/condition/EnabledOnRedisAvailable.java | 2 +- .../test/condition/EnabledOnRedisAvailableCondition.java | 2 +- .../redis/test/condition/EnabledOnRedisClusterAvailable.java | 2 +- .../redis/test/condition/EnabledOnRedisClusterCondition.java | 2 +- .../data/redis/test/condition/EnabledOnRedisDriver.java | 2 +- .../redis/test/condition/EnabledOnRedisDriverCondition.java | 2 +- .../redis/test/condition/EnabledOnRedisSentinelAvailable.java | 2 +- .../redis/test/condition/EnabledOnRedisSentinelCondition.java | 2 +- .../data/redis/test/condition/EnabledOnRedisVersion.java | 2 +- .../redis/test/condition/EnabledOnRedisVersionCondition.java | 2 +- .../data/redis/test/condition/LongRunningTest.java | 2 +- .../data/redis/test/condition/RedisConditions.java | 2 +- .../data/redis/test/condition/RedisDetector.java | 2 +- .../data/redis/test/condition/RedisDriver.java | 2 +- .../data/redis/test/extension/JedisExtension.java | 2 +- .../data/redis/test/extension/LettuceExtension.java | 2 +- .../data/redis/test/extension/LettuceTestClientResources.java | 2 +- .../data/redis/test/extension/RedisCluster.java | 2 +- .../data/redis/test/extension/RedisSentinel.java | 2 +- .../data/redis/test/extension/RedisStanalone.java | 2 +- .../data/redis/test/extension/ShutdownQueue.java | 2 +- .../test/extension/parametrized/MethodArgumentsProvider.java | 2 +- .../data/redis/test/extension/parametrized/MethodSource.java | 2 +- .../test/extension/parametrized/ParameterizedRedisTest.java | 2 +- .../parametrized/ParameterizedRedisTestExtension.java | 2 +- .../test/extension/parametrized/ParameterizedTestContext.java | 2 +- .../parametrized/ParameterizedTestInvocationContext.java | 2 +- .../parametrized/ParameterizedTestNameFormatter.java | 2 +- .../parametrized/ParameterizedTestParameterResolver.java | 2 +- .../data/redis/test/util/CollectionAwareComparator.java | 2 +- .../springframework/data/redis/test/util/HexStringUtils.java | 2 +- .../springframework/data/redis/test/util/MockitoUtils.java | 2 +- .../springframework/data/redis/test/util/RedisTestData.java | 2 +- .../springframework/data/redis/util/ByteUtilsUnitTests.java | 2 +- .../springframework/data/redis/util/ConnectionVerifier.java | 2 +- .../data/redis/core/PartialUpdateExtensionsUnitTests.kt | 2 +- .../redis/core/ReactiveGeoOperationsExtensionsUnitTests.kt | 2 +- .../redis/core/ReactiveHashOperationsExtensionsUnitTests.kt | 2 +- .../core/ReactiveHyperLogLogOperationsExtensionsUnitTests.kt | 2 +- .../redis/core/ReactiveListOperationsExtensionsUnitTests.kt | 2 +- .../redis/core/ReactiveRedisOperationsExtensionsUnitTests.kt | 2 +- .../redis/core/ReactiveSetOperationsExtensionsUnitTests.kt | 2 +- .../redis/core/ReactiveStreamOperationsExtensionsUnitTests.kt | 2 +- .../redis/core/ReactiveValueOperationsExtensionsUnitTests.kt | 2 +- .../redis/core/ReactiveZSetOperationsExtensionsUnitTests.kt | 2 +- .../data/redis/core/script/RedisScriptExtensionsUnitTests.kt | 2 +- 867 files changed, 868 insertions(+), 868 deletions(-) diff --git a/src/main/antora/modules/ROOT/pages/redis/cluster.adoc b/src/main/antora/modules/ROOT/pages/redis/cluster.adoc index e32c781614..9e1e18f68c 100644 --- a/src/main/antora/modules/ROOT/pages/redis/cluster.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/cluster.adoc @@ -37,8 +37,8 @@ The following example shows a set of commands being run across the cluster: redis-cli@127.0.0.1:7379 > cluster nodes 6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460 <1> -7bb78c... 127.0.0.1:7380 master - 0 1449730618304 2 connected 5461-20242 <2> -164888... 127.0.0.1:7381 master - 0 1449730618304 3 connected 10923-20243 <3> +7bb78c... 127.0.0.1:7380 master - 0 1449730618304 2 connected 5461-20252 <2> +164888... 127.0.0.1:7381 master - 0 1449730618304 3 connected 10923-20253 <3> b8b5ee... 127.0.0.1:7382 slave 6b38bb... 0 1449730618304 25 connected <4> ---- diff --git a/src/main/antora/modules/ROOT/pages/redis/redis-repositories/usage.adoc b/src/main/antora/modules/ROOT/pages/redis/redis-repositories/usage.adoc index ca6a9c41b1..e7f4fae343 100644 --- a/src/main/antora/modules/ROOT/pages/redis/redis-repositories/usage.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/redis-repositories/usage.adoc @@ -87,7 +87,7 @@ public void basicCrudOperations() { } ---- -<1> Generates a new `id` if the current value is `null` or reuses an already set `id` value and stores properties of type `Person` inside the Redis Hash with a key that has a pattern of `keyspace:id` -- in this case, it might be `people:5d67b7e1-8640-2024-beeb-c666fab4c0e5`. +<1> Generates a new `id` if the current value is `null` or reuses an already set `id` value and stores properties of type `Person` inside the Redis Hash with a key that has a pattern of `keyspace:id` -- in this case, it might be `people:5d67b7e1-8640-2025-beeb-c666fab4c0e5`. <2> Uses the provided `id` to retrieve the object stored at `keyspace:id`. <3> Counts the total number of entities available within the keyspace, `people`, defined by `@RedisHash` on `Person`. <4> Removes the key for the given object from Redis. diff --git a/src/main/java/org/springframework/data/redis/ClusterRedirectException.java b/src/main/java/org/springframework/data/redis/ClusterRedirectException.java index ee90d0f890..562347e86a 100644 --- a/src/main/java/org/springframework/data/redis/ClusterRedirectException.java +++ b/src/main/java/org/springframework/data/redis/ClusterRedirectException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/ClusterStateFailureException.java b/src/main/java/org/springframework/data/redis/ClusterStateFailureException.java index d3867139c0..29543870d6 100644 --- a/src/main/java/org/springframework/data/redis/ClusterStateFailureException.java +++ b/src/main/java/org/springframework/data/redis/ClusterStateFailureException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/ExceptionTranslationStrategy.java b/src/main/java/org/springframework/data/redis/ExceptionTranslationStrategy.java index d5c34e9cdb..a389a4c814 100644 --- a/src/main/java/org/springframework/data/redis/ExceptionTranslationStrategy.java +++ b/src/main/java/org/springframework/data/redis/ExceptionTranslationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/FallbackExceptionTranslationStrategy.java b/src/main/java/org/springframework/data/redis/FallbackExceptionTranslationStrategy.java index ed9728797f..58eb89e21b 100644 --- a/src/main/java/org/springframework/data/redis/FallbackExceptionTranslationStrategy.java +++ b/src/main/java/org/springframework/data/redis/FallbackExceptionTranslationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/PassThroughExceptionTranslationStrategy.java b/src/main/java/org/springframework/data/redis/PassThroughExceptionTranslationStrategy.java index aa4aeee609..55da37766b 100644 --- a/src/main/java/org/springframework/data/redis/PassThroughExceptionTranslationStrategy.java +++ b/src/main/java/org/springframework/data/redis/PassThroughExceptionTranslationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/RedisConnectionFailureException.java b/src/main/java/org/springframework/data/redis/RedisConnectionFailureException.java index 73d4fe2045..044a13126b 100644 --- a/src/main/java/org/springframework/data/redis/RedisConnectionFailureException.java +++ b/src/main/java/org/springframework/data/redis/RedisConnectionFailureException.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/RedisSystemException.java b/src/main/java/org/springframework/data/redis/RedisSystemException.java index 0767a37698..64230b481f 100644 --- a/src/main/java/org/springframework/data/redis/RedisSystemException.java +++ b/src/main/java/org/springframework/data/redis/RedisSystemException.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/TooManyClusterRedirectionsException.java b/src/main/java/org/springframework/data/redis/TooManyClusterRedirectionsException.java index db4b3026b4..e985374ae7 100644 --- a/src/main/java/org/springframework/data/redis/TooManyClusterRedirectionsException.java +++ b/src/main/java/org/springframework/data/redis/TooManyClusterRedirectionsException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/aot/RedisRuntimeHints.java b/src/main/java/org/springframework/data/redis/aot/RedisRuntimeHints.java index 0649d513e6..efa2669e3c 100644 --- a/src/main/java/org/springframework/data/redis/aot/RedisRuntimeHints.java +++ b/src/main/java/org/springframework/data/redis/aot/RedisRuntimeHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/BatchStrategies.java b/src/main/java/org/springframework/data/redis/cache/BatchStrategies.java index 047ef3834c..dc808e516e 100644 --- a/src/main/java/org/springframework/data/redis/cache/BatchStrategies.java +++ b/src/main/java/org/springframework/data/redis/cache/BatchStrategies.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/BatchStrategy.java b/src/main/java/org/springframework/data/redis/cache/BatchStrategy.java index d18db54c9a..98b3b32360 100644 --- a/src/main/java/org/springframework/data/redis/cache/BatchStrategy.java +++ b/src/main/java/org/springframework/data/redis/cache/BatchStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/CacheKeyPrefix.java b/src/main/java/org/springframework/data/redis/cache/CacheKeyPrefix.java index a08003f2d2..6d340afdad 100644 --- a/src/main/java/org/springframework/data/redis/cache/CacheKeyPrefix.java +++ b/src/main/java/org/springframework/data/redis/cache/CacheKeyPrefix.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/CacheStatistics.java b/src/main/java/org/springframework/data/redis/cache/CacheStatistics.java index d69d768508..7e92fa7835 100644 --- a/src/main/java/org/springframework/data/redis/cache/CacheStatistics.java +++ b/src/main/java/org/springframework/data/redis/cache/CacheStatistics.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/CacheStatisticsCollector.java b/src/main/java/org/springframework/data/redis/cache/CacheStatisticsCollector.java index 24b5eff794..d0ab23a6e3 100644 --- a/src/main/java/org/springframework/data/redis/cache/CacheStatisticsCollector.java +++ b/src/main/java/org/springframework/data/redis/cache/CacheStatisticsCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/CacheStatisticsProvider.java b/src/main/java/org/springframework/data/redis/cache/CacheStatisticsProvider.java index b7566f4212..2a4eed63de 100644 --- a/src/main/java/org/springframework/data/redis/cache/CacheStatisticsProvider.java +++ b/src/main/java/org/springframework/data/redis/cache/CacheStatisticsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/DefaultCacheStatisticsCollector.java b/src/main/java/org/springframework/data/redis/cache/DefaultCacheStatisticsCollector.java index 6a8d5b7620..693a8e945b 100644 --- a/src/main/java/org/springframework/data/redis/cache/DefaultCacheStatisticsCollector.java +++ b/src/main/java/org/springframework/data/redis/cache/DefaultCacheStatisticsCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java index d8e1b831e7..7d5b291404 100644 --- a/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/FixedDurationTtlFunction.java b/src/main/java/org/springframework/data/redis/cache/FixedDurationTtlFunction.java index e0a328e878..6c456de4c8 100644 --- a/src/main/java/org/springframework/data/redis/cache/FixedDurationTtlFunction.java +++ b/src/main/java/org/springframework/data/redis/cache/FixedDurationTtlFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/MutableCacheStatistics.java b/src/main/java/org/springframework/data/redis/cache/MutableCacheStatistics.java index a783da8c8f..94a19b1700 100644 --- a/src/main/java/org/springframework/data/redis/cache/MutableCacheStatistics.java +++ b/src/main/java/org/springframework/data/redis/cache/MutableCacheStatistics.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/NoOpCacheStatisticsCollector.java b/src/main/java/org/springframework/data/redis/cache/NoOpCacheStatisticsCollector.java index 498ff82679..4180ce875d 100644 --- a/src/main/java/org/springframework/data/redis/cache/NoOpCacheStatisticsCollector.java +++ b/src/main/java/org/springframework/data/redis/cache/NoOpCacheStatisticsCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCache.java b/src/main/java/org/springframework/data/redis/cache/RedisCache.java index 41cd0330b2..8bf70e0853 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCache.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java index 0d32f86a0d..56022f81f9 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java index 23d51456c7..3c9e3c5415 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java index 61e7f49d1d..87339d66a2 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/config/RedisCollectionParser.java b/src/main/java/org/springframework/data/redis/config/RedisCollectionParser.java index 6e5963eb9f..bb50defa3e 100644 --- a/src/main/java/org/springframework/data/redis/config/RedisCollectionParser.java +++ b/src/main/java/org/springframework/data/redis/config/RedisCollectionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/config/RedisListenerContainerParser.java b/src/main/java/org/springframework/data/redis/config/RedisListenerContainerParser.java index 7c09a8896d..c69d645e7b 100644 --- a/src/main/java/org/springframework/data/redis/config/RedisListenerContainerParser.java +++ b/src/main/java/org/springframework/data/redis/config/RedisListenerContainerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/config/RedisNamespaceHandler.java b/src/main/java/org/springframework/data/redis/config/RedisNamespaceHandler.java index 256dba0ffd..5c4394020a 100644 --- a/src/main/java/org/springframework/data/redis/config/RedisNamespaceHandler.java +++ b/src/main/java/org/springframework/data/redis/config/RedisNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/AbstractRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/AbstractRedisConnection.java index 7b2581973d..46a7a12ded 100644 --- a/src/main/java/org/springframework/data/redis/connection/AbstractRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/AbstractRedisConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java b/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java index 282893d6b6..b90121f90b 100644 --- a/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/BitFieldSubCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutionFailureException.java b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutionFailureException.java index 851a42cd1c..1ee5458ec3 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutionFailureException.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutionFailureException.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java index 4bbf07bc83..51c8c9372d 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterInfo.java b/src/main/java/org/springframework/data/redis/connection/ClusterInfo.java index e09ebbfd8d..8d6179811c 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterInfo.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterNodeResourceProvider.java b/src/main/java/org/springframework/data/redis/connection/ClusterNodeResourceProvider.java index 454ac41d23..a12ce3f8ea 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterNodeResourceProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterNodeResourceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterSlotHashUtil.java b/src/main/java/org/springframework/data/redis/connection/ClusterSlotHashUtil.java index 6494f423c7..58b49520bf 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterSlotHashUtil.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterSlotHashUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterTopology.java b/src/main/java/org/springframework/data/redis/connection/ClusterTopology.java index 1ea254bdb2..42436431f4 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterTopology.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterTopology.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterTopologyProvider.java b/src/main/java/org/springframework/data/redis/connection/ClusterTopologyProvider.java index 22827c1d2b..f03067e21f 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterTopologyProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterTopologyProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ConnectionUtils.java b/src/main/java/org/springframework/data/redis/connection/ConnectionUtils.java index 9c768307f4..58af2c6ba1 100644 --- a/src/main/java/org/springframework/data/redis/connection/ConnectionUtils.java +++ b/src/main/java/org/springframework/data/redis/connection/ConnectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/DataType.java b/src/main/java/org/springframework/data/redis/connection/DataType.java index 746eb09a9b..1b0635fbeb 100644 --- a/src/main/java/org/springframework/data/redis/connection/DataType.java +++ b/src/main/java/org/springframework/data/redis/connection/DataType.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/DecoratedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DecoratedRedisConnection.java index 2757810c0e..19ad271389 100644 --- a/src/main/java/org/springframework/data/redis/connection/DecoratedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DecoratedRedisConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultMessage.java b/src/main/java/org/springframework/data/redis/connection/DefaultMessage.java index 0ec00955e9..b96a0895f3 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultMessage.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultSortParameters.java b/src/main/java/org/springframework/data/redis/connection/DefaultSortParameters.java index 44526bd999..bd50f34ba6 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultSortParameters.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultSortParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java index 80cc128e55..8fe2f2c9f7 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringTuple.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringTuple.java index 7851a0f2a8..b35522c642 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringTuple.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringTuple.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisClusterConnection.java index dfa5986df5..369a272476 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisClusterConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java index aaeaafe18b..aa5f6de773 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/FutureResult.java b/src/main/java/org/springframework/data/redis/connection/FutureResult.java index 122e889798..58da880234 100644 --- a/src/main/java/org/springframework/data/redis/connection/FutureResult.java +++ b/src/main/java/org/springframework/data/redis/connection/FutureResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/Limit.java b/src/main/java/org/springframework/data/redis/connection/Limit.java index f47e2b6747..d6abf62b85 100644 --- a/src/main/java/org/springframework/data/redis/connection/Limit.java +++ b/src/main/java/org/springframework/data/redis/connection/Limit.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/Message.java b/src/main/java/org/springframework/data/redis/connection/Message.java index 5be91f3897..138f61f3b2 100644 --- a/src/main/java/org/springframework/data/redis/connection/Message.java +++ b/src/main/java/org/springframework/data/redis/connection/Message.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/MessageListener.java b/src/main/java/org/springframework/data/redis/connection/MessageListener.java index fcf894a413..4857b45c6d 100644 --- a/src/main/java/org/springframework/data/redis/connection/MessageListener.java +++ b/src/main/java/org/springframework/data/redis/connection/MessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/NamedNode.java b/src/main/java/org/springframework/data/redis/connection/NamedNode.java index a638ed5781..5ccf100762 100644 --- a/src/main/java/org/springframework/data/redis/connection/NamedNode.java +++ b/src/main/java/org/springframework/data/redis/connection/NamedNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/PoolException.java b/src/main/java/org/springframework/data/redis/connection/PoolException.java index 06969e9ace..1dc6b89cbb 100644 --- a/src/main/java/org/springframework/data/redis/connection/PoolException.java +++ b/src/main/java/org/springframework/data/redis/connection/PoolException.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterCommands.java index da4fad9d76..d780acced3 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterGeoCommands.java index d4496375cd..ae97652ba0 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterGeoCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterGeoCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterHashCommands.java index 60e2390988..6674520b33 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterHashCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterHyperLogLogCommands.java index 03df631960..2623eeda84 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterHyperLogLogCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterHyperLogLogCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterKeyCommands.java index 21aea13ba1..d650b738eb 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterListCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterListCommands.java index ad5bf5a3dc..f331728178 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterListCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterNumberCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterNumberCommands.java index 5968745c1c..3840a33b32 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterNumberCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterNumberCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterScriptingCommands.java index b027421546..a7e0f171c7 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterScriptingCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterServerCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterServerCommands.java index 050715b734..2a19d5fcfe 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterServerCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterSetCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterSetCommands.java index 8590edeab9..b20809fd24 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterStreamCommands.java index 696958fd77..d2dacc7fad 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterStreamCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterStringCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterStringCommands.java index 3800aec877..a410627a3a 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterStringCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterZSetCommands.java index f11a90d7cb..167d0f59b1 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveClusterZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveClusterZSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveGeoCommands.java index cbeec12b28..80c9fde257 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveGeoCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveGeoCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java index d75c2242a0..0fae8d30b8 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHyperLogLogCommands.java index d10975851a..cba1cc8fac 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveHyperLogLogCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHyperLogLogCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java index bd3f4d73dd..91b81640c1 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveListCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveListCommands.java index f3fbd20f3e..d92a99a270 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveListCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveNumberCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveNumberCommands.java index bbade90af8..31f80cc460 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveNumberCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveNumberCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactivePubSubCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactivePubSubCommands.java index d07155b4ab..5ea9d74885 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactivePubSubCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactivePubSubCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveRedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/ReactiveRedisClusterConnection.java index 2dc4efd081..cec6b7991b 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveRedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveRedisClusterConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/ReactiveRedisConnection.java index 7da6929ad3..5dfb6d9db8 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveRedisConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveRedisConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/ReactiveRedisConnectionFactory.java index 05e726e753..3492913549 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveRedisConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveRedisConnectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveScriptingCommands.java index 5337763728..4ad5d59004 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveScriptingCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveServerCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveServerCommands.java index d95b718503..c0d65c1a81 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveServerCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveSetCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveSetCommands.java index 845301dba9..76911fd441 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java index db05734500..2860dc691c 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStreamCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java index f6d18dbcfb..ab324fb46e 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveSubscription.java b/src/main/java/org/springframework/data/redis/connection/ReactiveSubscription.java index 2929544c97..153d139e8f 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveSubscription.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveSubscription.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java index 2abbb212ce..5400250eb9 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterCommands.java index 9a47dd7413..018a78b6cb 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterCommandsProvider.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterCommandsProvider.java index 7427559004..e26842e779 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterCommandsProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterCommandsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java index 62d9b6d140..8960d32ae1 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterConnection.java index d079fae2f3..585780ecb1 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterNode.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterNode.java index 99cd937075..7a6443d484 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterNode.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterServerCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterServerCommands.java index 0802c5a7ac..1bcac4c1f7 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterServerCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisCommands.java index 2b60126aad..1ebb48b83f 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisCommandsProvider.java b/src/main/java/org/springframework/data/redis/connection/RedisCommandsProvider.java index 0a1c7a18ed..24cfc387f9 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisCommandsProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisCommandsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java index 395ccfc85f..053ce917de 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisConnection.java b/src/main/java/org/springframework/data/redis/connection/RedisConnection.java index b3021e64ef..69917d5391 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisConnectionCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisConnectionCommands.java index 704da97a77..74875a4bb2 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisConnectionCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisConnectionCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/RedisConnectionFactory.java index 88f74d8a56..d694630701 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisConnectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisGeoCommands.java index 815363f36c..fce11eb2f6 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisGeoCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisGeoCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java index d1587a7918..6385c56a57 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHyperLogLogCommands.java index de24aff2d7..67fa08a4ad 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisHyperLogLogCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisHyperLogLogCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisInvalidSubscriptionException.java b/src/main/java/org/springframework/data/redis/connection/RedisInvalidSubscriptionException.java index 0af5905aec..d638a90de7 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisInvalidSubscriptionException.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisInvalidSubscriptionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java index ceaf0025be..414f178d92 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisListCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisListCommands.java index 8d174c89b8..18852d2f17 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisListCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisNode.java b/src/main/java/org/springframework/data/redis/connection/RedisNode.java index fa04f94245..3e117e0d36 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisNode.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisPassword.java b/src/main/java/org/springframework/data/redis/connection/RedisPassword.java index 56229c84ff..3fcb4ce343 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisPassword.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisPassword.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisPipelineException.java b/src/main/java/org/springframework/data/redis/connection/RedisPipelineException.java index 9a6e31ca16..0f4cd4c9e5 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisPipelineException.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisPipelineException.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisPubSubCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisPubSubCommands.java index 6f25eb7daa..00283e80ce 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisPubSubCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisPubSubCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisScriptingCommands.java index 1fd9e96afd..ce54c205cd 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisScriptingCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSentinelCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisSentinelCommands.java index 25f3fb666f..0a3882e643 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSentinelCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSentinelCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java index f0dedc2e74..5839a8f701 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConnection.java b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConnection.java index 0d84cc2107..34f0db6003 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisServer.java b/src/main/java/org/springframework/data/redis/connection/RedisServer.java index 1a8b306c45..bd363598c6 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisServer.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisServerCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisServerCommands.java index 26a610e05c..f21e6281fd 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisServerCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSetCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisSetCommands.java index bd3e75deb8..26dcb1976f 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java index 4f9a0336db..e48799a659 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java index d106dda372..8acb2a3be8 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java index f3033f8176..66dd6f6051 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java index df097158b0..8385d70d34 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStreamCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java index fe2abfce88..007bbc774c 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSubscribedConnectionException.java b/src/main/java/org/springframework/data/redis/connection/RedisSubscribedConnectionException.java index 56ce5a9f35..846a935c4d 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSubscribedConnectionException.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSubscribedConnectionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisTxCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisTxCommands.java index 8e3578dbc2..56ad3634ea 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisTxCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisTxCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java index 8578d688f6..6ad0ecd59b 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ReturnType.java b/src/main/java/org/springframework/data/redis/connection/ReturnType.java index 5ed9e24dbf..2ba37e2ab9 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReturnType.java +++ b/src/main/java/org/springframework/data/redis/connection/ReturnType.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java b/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java index 10954f4caa..ce778884d7 100644 --- a/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java +++ b/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/SortParameters.java b/src/main/java/org/springframework/data/redis/connection/SortParameters.java index b90d56c6a9..27804b98fe 100644 --- a/src/main/java/org/springframework/data/redis/connection/SortParameters.java +++ b/src/main/java/org/springframework/data/redis/connection/SortParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java index e198eecfd3..2c286ce97e 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/Subscription.java b/src/main/java/org/springframework/data/redis/connection/Subscription.java index 8fbbb32239..96510df79c 100644 --- a/src/main/java/org/springframework/data/redis/connection/Subscription.java +++ b/src/main/java/org/springframework/data/redis/connection/Subscription.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/SubscriptionListener.java b/src/main/java/org/springframework/data/redis/connection/SubscriptionListener.java index 5049ef2423..8d9e9d6b00 100644 --- a/src/main/java/org/springframework/data/redis/connection/SubscriptionListener.java +++ b/src/main/java/org/springframework/data/redis/connection/SubscriptionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/ValueEncoding.java b/src/main/java/org/springframework/data/redis/connection/ValueEncoding.java index 1185756bf3..b0d573639a 100644 --- a/src/main/java/org/springframework/data/redis/connection/ValueEncoding.java +++ b/src/main/java/org/springframework/data/redis/connection/ValueEncoding.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 2dc686bcf9..2d4eb2cb59 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/convert/ListConverter.java b/src/main/java/org/springframework/data/redis/connection/convert/ListConverter.java index d9b87a8c86..1a384f1af7 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/ListConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/ListConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/convert/LongToBooleanConverter.java b/src/main/java/org/springframework/data/redis/connection/convert/LongToBooleanConverter.java index 0e708780bc..8686258820 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/LongToBooleanConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/LongToBooleanConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/convert/MapConverter.java b/src/main/java/org/springframework/data/redis/connection/convert/MapConverter.java index 2d9bafca65..1f09154cba 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/MapConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/MapConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/convert/MapToPropertiesConverter.java b/src/main/java/org/springframework/data/redis/connection/convert/MapToPropertiesConverter.java index 2e1e93e155..b25e587e0a 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/MapToPropertiesConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/MapToPropertiesConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 244306125e..c51002e368 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/convert/StringToDataTypeConverter.java b/src/main/java/org/springframework/data/redis/connection/convert/StringToDataTypeConverter.java index 2d1ea32cc3..39214821ca 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/StringToDataTypeConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/StringToDataTypeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/convert/StringToPropertiesConverter.java b/src/main/java/org/springframework/data/redis/connection/convert/StringToPropertiesConverter.java index 7f8889eabc..16725f049c 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/StringToPropertiesConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/StringToPropertiesConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 d2bf3aa39e..4c01ddfe6a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/convert/TransactionResultConverter.java b/src/main/java/org/springframework/data/redis/connection/convert/TransactionResultConverter.java index 883ceb9e15..2655245ceb 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/TransactionResultConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/TransactionResultConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java index 683c0ec55a..fee76a291f 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java index 8f508c374f..44e8855bf8 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java index b6b9a22467..6cf9357dfa 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 af7707d9ae..ab8d1d072b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java index 9dc9af5cbb..51f6f3cd14 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterGeoCommands.java index 1d98726325..35c7db73f3 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterGeoCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterGeoCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java index 1803da058a..47ad6c6eec 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHyperLogLogCommands.java index 87fdfc25d9..dd77b33f58 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHyperLogLogCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHyperLogLogCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java index f431d362ec..cb1b07e9c3 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterListCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterListCommands.java index 81f93645b8..3b259f36e1 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterListCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java index 727c4536d9..9e845f032e 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 a48a35092a..1c6045faf9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java index 4be0422e03..c25def525f 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStreamCommands.java index 05cadae499..9d26a6cd8d 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStreamCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 514ce81579..ba02de01a0 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 @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 5d9970973a..b7bc9b5f92 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 @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 840f49dd04..3ec675dfdd 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 @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java index c2ecc3e2cf..419b82550e 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 6ffa002cbe..6be56d07b0 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 743565b76a..50d594afa7 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 a5f806974d..e034cefb0b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java index be2cf8bb90..a0ac8debf2 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHyperLogLogCommands.java index 64e7dc92e1..25393cf2af 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHyperLogLogCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHyperLogLogCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisInvoker.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisInvoker.java index 8d7a1cf2d3..586a35eca2 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisInvoker.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java index 58fc4e2408..93f0ddfff6 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisListCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisListCommands.java index fc3e54259a..a7bb1dd619 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisListCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisMessageListener.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisMessageListener.java index e44685e32b..2092efeaf1 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisMessageListener.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisMessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisResult.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisResult.java index ca68b02d43..16b1483103 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisResult.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptReturnConverter.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptReturnConverter.java index d7a2223a18..0398623c5e 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptReturnConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptReturnConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java index 0c114dacb5..5f7b76d592 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisScriptingCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisSentinelConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisSentinelConnection.java index 8af29b6792..15dcb28db0 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisSentinelConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisSentinelConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 3aa8fb9eab..a7e0feca2b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisSetCommands.java index c9ed8280de..c2521b82db 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStreamCommands.java index c3ecbe8255..483b299c9c 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStreamCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java index 889e87b102..9b21e8e715 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisSubscription.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisSubscription.java index 46a9b49668..6d34e4628d 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisSubscription.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisSubscription.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 cbb4eb40d8..e3cef50537 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 @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/StreamConverters.java b/src/main/java/org/springframework/data/redis/connection/jedis/StreamConverters.java index f4b0c7b2ce..a68eef451a 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/StreamConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/StreamConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java index 3426bb4cf2..60564519fa 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java index 05035f5fae..787e278d95 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettuceClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java index 6a89a2bd3d..40d02becd4 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceByteBufferPubSubListenerWrapper.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceByteBufferPubSubListenerWrapper.java index d76cc2aee1..c4bdc997a2 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceByteBufferPubSubListenerWrapper.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceByteBufferPubSubListenerWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java index 91e243ffaa..4cc752877d 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java index f02cfe00e7..41a7cb94e1 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterGeoCommands.java index d220ed3cb4..96196ca6a6 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterGeoCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterGeoCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterHashCommands.java index 29f36d29e7..aa5db78bac 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterHashCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterHyperLogLogCommands.java index d8215c338f..f0fe06ccdb 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterHyperLogLogCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterHyperLogLogCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterKeyCommands.java index ae3d3b2e49..09eabc950e 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterListCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterListCommands.java index f7905764e5..9c27d41b64 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterListCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterServerCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterServerCommands.java index 6d2e66cda0..d17f801899 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterServerCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterSetCommands.java index 3a346141c2..2ff5bb3a84 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterStringCommands.java index 4b6ae60f04..986569e55d 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterStringCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterTopologyProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterTopologyProvider.java index 5176bf68e1..fb789b056c 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterTopologyProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterTopologyProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterZSetCommands.java index fa80eeda87..a99532618e 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterZSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java index bbf1755ab6..fc8460d514 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java index 6d54316548..9bb8644a9a 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionProvider.java index cdfcf5c980..ad352da67d 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java index bb443b1166..5548d2d1f6 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceExceptionConverter.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceExceptionConverter.java index 1b0a27441c..773f0a7522 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceExceptionConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceExceptionConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceFutureUtils.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceFutureUtils.java index 764f6e7ff9..556d37b8f7 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceFutureUtils.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceFutureUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceGeoCommands.java index f0ff6e2715..0cad933193 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceGeoCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceGeoCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java index 5125a82fb6..e4b53f4fb4 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHyperLogLogCommands.java index 12fbbd5be2..d62e3b9d5e 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHyperLogLogCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHyperLogLogCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceInvoker.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceInvoker.java index 7661db4a73..451593fc19 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceInvoker.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java index 415da2ccaa..a9514cd793 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceListCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceListCommands.java index cc1be60431..4f930633f2 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceListCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceMessageListener.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceMessageListener.java index 2c4b021bbd..1451a2829d 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceMessageListener.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceMessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfiguration.java index c9fa6c242e..bb72d32ccc 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java index 79e6b5e836..88ef7561ab 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterGeoCommands.java index c797927296..d682898458 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterGeoCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterGeoCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHashCommands.java index 9b5969d88d..70e5f200bc 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHashCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHyperLogLogCommands.java index 999207aa75..788b00e0c1 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHyperLogLogCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHyperLogLogCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterKeyCommands.java index 87c34658f8..ed7dd87242 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterListCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterListCommands.java index 2001abdf17..0d1f56c7b5 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterListCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterNumberCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterNumberCommands.java index d25f6c1c95..d7dde2e6ed 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterNumberCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterNumberCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterScriptingCommands.java index e4907f90ce..8f084c5783 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterScriptingCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterServerCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterServerCommands.java index fca3ee62de..e0f12524e2 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterServerCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterSetCommands.java index be5790956f..85d6feb3c1 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStreamCommands.java index 9de831682a..1de72bfbc1 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStreamCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommands.java index b0d4e6a1b8..5c466dc9ab 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterZSetCommands.java index ccd8de9354..a4f1ad2288 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterZSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveGeoCommands.java index 4502f5eb07..9425570f4a 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveGeoCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveGeoCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java index 0837489840..b704321ef5 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHyperLogLogCommands.java index f4cd6e9186..f7db48edd9 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHyperLogLogCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHyperLogLogCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java index a7df677064..a1371b7856 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java index e0f6f416c3..77e6280491 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveNumberCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveNumberCommands.java index fd4b0de4f6..f05cff7134 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveNumberCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveNumberCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommands.java index 67ca0b0b81..63fd654e10 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisClusterConnection.java index a688e94060..b66d0827a0 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisClusterConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisConnection.java index b99c098096..6905356065 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommands.java index c6d5686486..ec89c3f831 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommands.java index c88df347e0..9b8f22f54a 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSetCommands.java index 8fd0d4f403..d74953a6b7 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommands.java index e22b298146..1f45c373cd 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java index 7beb8a3bd2..eaec33cb20 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSubscription.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSubscription.java index 9098c14c84..9df426a53d 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSubscription.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSubscription.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java index d11f6229be..415d76bfe2 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceResult.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceResult.java index 4c0a825c85..d3e2f779a1 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceResult.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScanCursor.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScanCursor.java index 50ae5712bc..b3b467533c 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScanCursor.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScanCursor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScriptingCommands.java index de38a44747..52ec10e0ed 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceScriptingCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelConnection.java index 79bf304a91..1abd08e60e 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java index 005fc8c38f..1b9124e138 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSetCommands.java index 3e210bdedd..cb165702ff 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStreamCommands.java index 7f15f68e8e..ad5c2281c2 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStreamCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStreamCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java index 1397fc9ce7..ebaeb7475f 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSubscription.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSubscription.java index 72acfef38f..32b0d97397 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSubscription.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSubscription.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java index e29aa50dc5..aeb16f6e56 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/RangeConverter.java b/src/main/java/org/springframework/data/redis/connection/lettuce/RangeConverter.java index 7777c165d9..9210d2d908 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/RangeConverter.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/RangeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/RedisClientProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/RedisClientProvider.java index 637999d1d9..02c8ec152a 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/RedisClientProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/RedisClientProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/RedisCredentialsProviderFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/RedisCredentialsProviderFactory.java index e34b8f5757..b29341b22b 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/RedisCredentialsProviderFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/RedisCredentialsProviderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/StandaloneConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/StandaloneConnectionProvider.java index 15bcd3d1bb..e812b4946b 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/StandaloneConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/StandaloneConnectionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProvider.java index c77d4d077f..562c2eef31 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/StreamConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/StreamConverters.java index a243a44748..ae17a9ca70 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/StreamConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/StreamConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/aot/LettuceRuntimeHints.java b/src/main/java/org/springframework/data/redis/connection/lettuce/aot/LettuceRuntimeHints.java index fc9d3ab70a..337b000195 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/aot/LettuceRuntimeHints.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/aot/LettuceRuntimeHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java index 1f21776ed2..ca4fdc65e5 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java index 0acb956297..eb1321f511 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java index 55a9316c14..ea01647152 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java index 2f974056b0..bbf3d28fe6 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java index 6654be9bd1..989f2cece8 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java index f2c311266d..fdaf1c27e1 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/ByteBufferRecord.java b/src/main/java/org/springframework/data/redis/connection/stream/ByteBufferRecord.java index 990dc0ffc6..488ad046fc 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/ByteBufferRecord.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/ByteBufferRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/ByteRecord.java b/src/main/java/org/springframework/data/redis/connection/stream/ByteRecord.java index f2d97ca812..25d625ae6d 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/ByteRecord.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/ByteRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/Consumer.java b/src/main/java/org/springframework/data/redis/connection/stream/Consumer.java index 0b43ba5933..b5c05e1e03 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/Consumer.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/Consumer.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/MapRecord.java b/src/main/java/org/springframework/data/redis/connection/stream/MapRecord.java index 731af14240..baf61286b6 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/MapRecord.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/MapRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/ObjectRecord.java b/src/main/java/org/springframework/data/redis/connection/stream/ObjectRecord.java index 62c42775a6..7cb685dfb9 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/ObjectRecord.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/ObjectRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/PendingMessage.java b/src/main/java/org/springframework/data/redis/connection/stream/PendingMessage.java index 7358645534..7dfb35d506 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/PendingMessage.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/PendingMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/PendingMessages.java b/src/main/java/org/springframework/data/redis/connection/stream/PendingMessages.java index 80a3e146db..50130ecc64 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/PendingMessages.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/PendingMessages.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/PendingMessagesSummary.java b/src/main/java/org/springframework/data/redis/connection/stream/PendingMessagesSummary.java index 5716b2f34f..1addb65e8c 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/PendingMessagesSummary.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/PendingMessagesSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/ReadOffset.java b/src/main/java/org/springframework/data/redis/connection/stream/ReadOffset.java index 7efec2110c..0e9563d5ce 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/ReadOffset.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/ReadOffset.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/Record.java b/src/main/java/org/springframework/data/redis/connection/stream/Record.java index 4c34d5ce87..9ad9a8cd6f 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/Record.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/Record.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/RecordId.java b/src/main/java/org/springframework/data/redis/connection/stream/RecordId.java index 5791a116d4..6e79c38cc7 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/RecordId.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/RecordId.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/StreamInfo.java b/src/main/java/org/springframework/data/redis/connection/stream/StreamInfo.java index c822fcea47..4a657a03f5 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/StreamInfo.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/StreamInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/StreamOffset.java b/src/main/java/org/springframework/data/redis/connection/stream/StreamOffset.java index 5ceb927972..df44bc663a 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/StreamOffset.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/StreamOffset.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/StreamReadOptions.java b/src/main/java/org/springframework/data/redis/connection/stream/StreamReadOptions.java index 58fd0652e8..4729e38a41 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/StreamReadOptions.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/StreamReadOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/StreamRecords.java b/src/main/java/org/springframework/data/redis/connection/stream/StreamRecords.java index 90de8d0d4e..499119a1be 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/StreamRecords.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/StreamRecords.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/StreamSerialization.java b/src/main/java/org/springframework/data/redis/connection/stream/StreamSerialization.java index 6558727a0b..6770bb3b77 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/StreamSerialization.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/StreamSerialization.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/stream/StringRecord.java b/src/main/java/org/springframework/data/redis/connection/stream/StringRecord.java index b1b8bce931..4d6c5a5bd3 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/StringRecord.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/StringRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/util/AbstractSubscription.java b/src/main/java/org/springframework/data/redis/connection/util/AbstractSubscription.java index d04adc7854..8ffb356306 100644 --- a/src/main/java/org/springframework/data/redis/connection/util/AbstractSubscription.java +++ b/src/main/java/org/springframework/data/redis/connection/util/AbstractSubscription.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/util/ByteArraySet.java b/src/main/java/org/springframework/data/redis/connection/util/ByteArraySet.java index fe964dd947..2bf6e29b55 100644 --- a/src/main/java/org/springframework/data/redis/connection/util/ByteArraySet.java +++ b/src/main/java/org/springframework/data/redis/connection/util/ByteArraySet.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/util/ByteArrayWrapper.java b/src/main/java/org/springframework/data/redis/connection/util/ByteArrayWrapper.java index 5a33c06642..883bc003c8 100644 --- a/src/main/java/org/springframework/data/redis/connection/util/ByteArrayWrapper.java +++ b/src/main/java/org/springframework/data/redis/connection/util/ByteArrayWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/util/DecodeUtils.java b/src/main/java/org/springframework/data/redis/connection/util/DecodeUtils.java index 3da38d5cad..9e688183d2 100644 --- a/src/main/java/org/springframework/data/redis/connection/util/DecodeUtils.java +++ b/src/main/java/org/springframework/data/redis/connection/util/DecodeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/zset/Aggregate.java b/src/main/java/org/springframework/data/redis/connection/zset/Aggregate.java index 083e386575..49a735eb6d 100644 --- a/src/main/java/org/springframework/data/redis/connection/zset/Aggregate.java +++ b/src/main/java/org/springframework/data/redis/connection/zset/Aggregate.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java b/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java index 4fa99efcef..fb16b9f66a 100644 --- a/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java +++ b/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/zset/Tuple.java b/src/main/java/org/springframework/data/redis/connection/zset/Tuple.java index a706af4a09..f1aca1f897 100644 --- a/src/main/java/org/springframework/data/redis/connection/zset/Tuple.java +++ b/src/main/java/org/springframework/data/redis/connection/zset/Tuple.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/connection/zset/Weights.java b/src/main/java/org/springframework/data/redis/connection/zset/Weights.java index 1ecab0fe37..754c48f230 100644 --- a/src/main/java/org/springframework/data/redis/connection/zset/Weights.java +++ b/src/main/java/org/springframework/data/redis/connection/zset/Weights.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/AbstractOperations.java b/src/main/java/org/springframework/data/redis/core/AbstractOperations.java index 3d0832e93b..c730e2d74b 100644 --- a/src/main/java/org/springframework/data/redis/core/AbstractOperations.java +++ b/src/main/java/org/springframework/data/redis/core/AbstractOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BoundGeoOperations.java b/src/main/java/org/springframework/data/redis/core/BoundGeoOperations.java index 4250560c0a..d11a387f80 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundGeoOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundGeoOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java index 90e77b9ae5..f906462911 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java b/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java index d719b05fb2..e9d1f5e57c 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java index 02d4f53091..481ee8e674 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java b/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java index 423cd3b4fd..3492553b28 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java +++ b/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BoundSetOperations.java b/src/main/java/org/springframework/data/redis/core/BoundSetOperations.java index 5d333bbc0d..424b210b67 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundSetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BoundStreamOperations.java b/src/main/java/org/springframework/data/redis/core/BoundStreamOperations.java index bbba7bf013..d3add2dad7 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundStreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundStreamOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java b/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java index b11d7c6657..40be5358f8 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java b/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java index 724cb5cd11..160d0836ad 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/BulkMapper.java b/src/main/java/org/springframework/data/redis/core/BulkMapper.java index 686b285f66..77fa487672 100644 --- a/src/main/java/org/springframework/data/redis/core/BulkMapper.java +++ b/src/main/java/org/springframework/data/redis/core/BulkMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/CloseSuppressingInvocationHandler.java b/src/main/java/org/springframework/data/redis/core/CloseSuppressingInvocationHandler.java index cdd8c26f1f..1f85f9d389 100644 --- a/src/main/java/org/springframework/data/redis/core/CloseSuppressingInvocationHandler.java +++ b/src/main/java/org/springframework/data/redis/core/CloseSuppressingInvocationHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ClusterOperations.java b/src/main/java/org/springframework/data/redis/core/ClusterOperations.java index 833ca61620..f96c8c0d39 100644 --- a/src/main/java/org/springframework/data/redis/core/ClusterOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ClusterOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ClusterOperationsEditor.java b/src/main/java/org/springframework/data/redis/core/ClusterOperationsEditor.java index 1f5fe55704..a662e7e437 100644 --- a/src/main/java/org/springframework/data/redis/core/ClusterOperationsEditor.java +++ b/src/main/java/org/springframework/data/redis/core/ClusterOperationsEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ConvertingCursor.java b/src/main/java/org/springframework/data/redis/core/ConvertingCursor.java index e80ea71d1a..b0d5e1e5fd 100644 --- a/src/main/java/org/springframework/data/redis/core/ConvertingCursor.java +++ b/src/main/java/org/springframework/data/redis/core/ConvertingCursor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/Cursor.java b/src/main/java/org/springframework/data/redis/core/Cursor.java index fa60b0e489..e52cfc5d77 100644 --- a/src/main/java/org/springframework/data/redis/core/Cursor.java +++ b/src/main/java/org/springframework/data/redis/core/Cursor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultClusterOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultClusterOperations.java index 77a285ae0f..7215430082 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultClusterOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultClusterOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultGeoOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultGeoOperations.java index e451cf97d7..9456bff358 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultGeoOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultGeoOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java index 1bfb9c3467..974e20e13f 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultHyperLogLogOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultHyperLogLogOperations.java index 41d8dc0406..30f0c4c3e9 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultHyperLogLogOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultHyperLogLogOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultListOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultListOperations.java index 61855b8b0d..ce0268869c 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultListOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveGeoOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveGeoOperations.java index 12192babd2..6e92f6a749 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveGeoOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveGeoOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java index fec1f2e7e1..c3e004c25d 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHyperLogLogOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHyperLogLogOperations.java index d9c3516741..c3acc12718 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHyperLogLogOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHyperLogLogOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveListOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveListOperations.java index 74297cd674..1a26f750a7 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveListOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveSetOperations.java index 13b546689e..4294bca879 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveSetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveStreamOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveStreamOperations.java index a3d7b93668..f7365adba5 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveStreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveStreamOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveValueOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveValueOperations.java index 0f0ac35200..4ed0483330 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveValueOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java index 5fc762f517..65ae76a139 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultSetOperations.java index 5949af8d73..9c8b87dad3 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultSetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultStreamOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultStreamOperations.java index a53b18d030..3cb27d1dcd 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultStreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultStreamOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultTypedTuple.java b/src/main/java/org/springframework/data/redis/core/DefaultTypedTuple.java index c26cdbe912..3ae83af6c5 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultTypedTuple.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultTypedTuple.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java index 6b857ee598..cbc44ca3c0 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java index 059f746707..030974bf8c 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/GeoOperations.java b/src/main/java/org/springframework/data/redis/core/GeoOperations.java index f06f2a88fc..6b20c07789 100644 --- a/src/main/java/org/springframework/data/redis/core/GeoOperations.java +++ b/src/main/java/org/springframework/data/redis/core/GeoOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/GeoOperationsEditor.java b/src/main/java/org/springframework/data/redis/core/GeoOperationsEditor.java index c55b5f55ab..5de01d1f56 100644 --- a/src/main/java/org/springframework/data/redis/core/GeoOperationsEditor.java +++ b/src/main/java/org/springframework/data/redis/core/GeoOperationsEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/HashMapperProvider.java b/src/main/java/org/springframework/data/redis/core/HashMapperProvider.java index 2b1b77547d..ae0df547b7 100644 --- a/src/main/java/org/springframework/data/redis/core/HashMapperProvider.java +++ b/src/main/java/org/springframework/data/redis/core/HashMapperProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/HashOperations.java b/src/main/java/org/springframework/data/redis/core/HashOperations.java index db97cdb10a..8a2c6641ad 100644 --- a/src/main/java/org/springframework/data/redis/core/HashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/HashOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/HashOperationsEditor.java b/src/main/java/org/springframework/data/redis/core/HashOperationsEditor.java index 30d31d81f2..8f8a1a751f 100644 --- a/src/main/java/org/springframework/data/redis/core/HashOperationsEditor.java +++ b/src/main/java/org/springframework/data/redis/core/HashOperationsEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/HyperLogLogOperations.java b/src/main/java/org/springframework/data/redis/core/HyperLogLogOperations.java index 0c16fa1be4..9adb6252f4 100644 --- a/src/main/java/org/springframework/data/redis/core/HyperLogLogOperations.java +++ b/src/main/java/org/springframework/data/redis/core/HyperLogLogOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/HyperLogLogOperationsEditor.java b/src/main/java/org/springframework/data/redis/core/HyperLogLogOperationsEditor.java index abf47663bc..4087bf24d4 100644 --- a/src/main/java/org/springframework/data/redis/core/HyperLogLogOperationsEditor.java +++ b/src/main/java/org/springframework/data/redis/core/HyperLogLogOperationsEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/IndexWriter.java b/src/main/java/org/springframework/data/redis/core/IndexWriter.java index c1060b08df..73e2a27347 100644 --- a/src/main/java/org/springframework/data/redis/core/IndexWriter.java +++ b/src/main/java/org/springframework/data/redis/core/IndexWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/KeyBoundCursor.java b/src/main/java/org/springframework/data/redis/core/KeyBoundCursor.java index 2cc66d5d79..a0c553509a 100644 --- a/src/main/java/org/springframework/data/redis/core/KeyBoundCursor.java +++ b/src/main/java/org/springframework/data/redis/core/KeyBoundCursor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/KeyScanOptions.java b/src/main/java/org/springframework/data/redis/core/KeyScanOptions.java index 0cbb843732..888157c525 100644 --- a/src/main/java/org/springframework/data/redis/core/KeyScanOptions.java +++ b/src/main/java/org/springframework/data/redis/core/KeyScanOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ListOperations.java b/src/main/java/org/springframework/data/redis/core/ListOperations.java index f453aba9a6..8d08c61aa1 100644 --- a/src/main/java/org/springframework/data/redis/core/ListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ListOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ListOperationsEditor.java b/src/main/java/org/springframework/data/redis/core/ListOperationsEditor.java index cf8fc18daa..4c7834d885 100644 --- a/src/main/java/org/springframework/data/redis/core/ListOperationsEditor.java +++ b/src/main/java/org/springframework/data/redis/core/ListOperationsEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/PartialUpdate.java b/src/main/java/org/springframework/data/redis/core/PartialUpdate.java index f516b1f65f..400333efdc 100644 --- a/src/main/java/org/springframework/data/redis/core/PartialUpdate.java +++ b/src/main/java/org/springframework/data/redis/core/PartialUpdate.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveGeoOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveGeoOperations.java index 76471777ae..b159d532f6 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveGeoOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveGeoOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java index 8e611e3594..2151590ecc 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveHyperLogLogOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveHyperLogLogOperations.java index e0ed1f6148..671a99b493 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveHyperLogLogOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveHyperLogLogOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java index 9862fedba6..f951a87287 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisCallback.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisCallback.java index 3a22e9a771..fe5be55d0a 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisCallback.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java index 052cc6d180..f027c46366 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisSessionCallback.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisSessionCallback.java index 3511b7ed02..8db54083fe 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisSessionCallback.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisSessionCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java index 6e52ef0e7f..92792ed81b 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveSetOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveSetOperations.java index f9d8ce7842..c3cbad9445 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveSetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java index 65697faf2a..341eafe6b7 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveStreamOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveStringRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/ReactiveStringRedisTemplate.java index a5a915e6e7..a593214d7f 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveStringRedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveStringRedisTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java index ace20be127..123376c1d2 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java index 1e77a2b1b4..eb7e882ed8 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisAccessor.java b/src/main/java/org/springframework/data/redis/core/RedisAccessor.java index 09c8decc24..5753347654 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisAccessor.java +++ b/src/main/java/org/springframework/data/redis/core/RedisAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisCallback.java b/src/main/java/org/springframework/data/redis/core/RedisCallback.java index b506d6875a..9ad8b41306 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisCallback.java +++ b/src/main/java/org/springframework/data/redis/core/RedisCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisClusterCallback.java b/src/main/java/org/springframework/data/redis/core/RedisClusterCallback.java index b9ac36c83d..f13ea55dd9 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisClusterCallback.java +++ b/src/main/java/org/springframework/data/redis/core/RedisClusterCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisCommand.java b/src/main/java/org/springframework/data/redis/core/RedisCommand.java index 1959d21128..e9303233df 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisCommand.java +++ b/src/main/java/org/springframework/data/redis/core/RedisCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java b/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java index 3996f3c982..8ae2902193 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java +++ b/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisHash.java b/src/main/java/org/springframework/data/redis/core/RedisHash.java index b0cb2a1ea1..4ffcb57d2e 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisHash.java +++ b/src/main/java/org/springframework/data/redis/core/RedisHash.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyExpiredEvent.java b/src/main/java/org/springframework/data/redis/core/RedisKeyExpiredEvent.java index a57f5fee93..3ebed4eb48 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyExpiredEvent.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyExpiredEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java index 69338587dd..ea74c8fc5c 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyValueTemplate.java b/src/main/java/org/springframework/data/redis/core/RedisKeyValueTemplate.java index edbe5c2ff6..4e1454f533 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyValueTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyValueTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyspaceEvent.java b/src/main/java/org/springframework/data/redis/core/RedisKeyspaceEvent.java index 3a5b6b342f..aff2138cb0 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyspaceEvent.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyspaceEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java index 38d2a39c52..8c1ad67ad6 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java b/src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java index 64dfe66e97..070e04d4ac 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java +++ b/src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java index 3afcf1f253..2879f161a4 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ScanCursor.java b/src/main/java/org/springframework/data/redis/core/ScanCursor.java index 2779d17c46..354e0cfcc9 100644 --- a/src/main/java/org/springframework/data/redis/core/ScanCursor.java +++ b/src/main/java/org/springframework/data/redis/core/ScanCursor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ScanIteration.java b/src/main/java/org/springframework/data/redis/core/ScanIteration.java index 1be8cf8771..3e0b5a5929 100644 --- a/src/main/java/org/springframework/data/redis/core/ScanIteration.java +++ b/src/main/java/org/springframework/data/redis/core/ScanIteration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ScanOptions.java b/src/main/java/org/springframework/data/redis/core/ScanOptions.java index 959aa9aaea..b7520703f6 100644 --- a/src/main/java/org/springframework/data/redis/core/ScanOptions.java +++ b/src/main/java/org/springframework/data/redis/core/ScanOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/SessionCallback.java b/src/main/java/org/springframework/data/redis/core/SessionCallback.java index 0d41a5299f..42a53a068f 100644 --- a/src/main/java/org/springframework/data/redis/core/SessionCallback.java +++ b/src/main/java/org/springframework/data/redis/core/SessionCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/SetOperations.java b/src/main/java/org/springframework/data/redis/core/SetOperations.java index 00471359d3..b1eddb91da 100644 --- a/src/main/java/org/springframework/data/redis/core/SetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/SetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/SetOperationsEditor.java b/src/main/java/org/springframework/data/redis/core/SetOperationsEditor.java index cad663aff8..a00ce61de0 100644 --- a/src/main/java/org/springframework/data/redis/core/SetOperationsEditor.java +++ b/src/main/java/org/springframework/data/redis/core/SetOperationsEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/StreamObjectMapper.java b/src/main/java/org/springframework/data/redis/core/StreamObjectMapper.java index d8e340f330..4b72930993 100644 --- a/src/main/java/org/springframework/data/redis/core/StreamObjectMapper.java +++ b/src/main/java/org/springframework/data/redis/core/StreamObjectMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/StreamOperations.java b/src/main/java/org/springframework/data/redis/core/StreamOperations.java index fa30db9f7d..59a0647f29 100644 --- a/src/main/java/org/springframework/data/redis/core/StreamOperations.java +++ b/src/main/java/org/springframework/data/redis/core/StreamOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/StreamOperationsEditor.java b/src/main/java/org/springframework/data/redis/core/StreamOperationsEditor.java index 5776342b2c..1dbb0b6bad 100644 --- a/src/main/java/org/springframework/data/redis/core/StreamOperationsEditor.java +++ b/src/main/java/org/springframework/data/redis/core/StreamOperationsEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/StringRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/StringRedisTemplate.java index 072872be57..5661d4f714 100644 --- a/src/main/java/org/springframework/data/redis/core/StringRedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/StringRedisTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/TimeToLive.java b/src/main/java/org/springframework/data/redis/core/TimeToLive.java index bcc148c608..c3f568a947 100644 --- a/src/main/java/org/springframework/data/redis/core/TimeToLive.java +++ b/src/main/java/org/springframework/data/redis/core/TimeToLive.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/TimeToLiveAccessor.java b/src/main/java/org/springframework/data/redis/core/TimeToLiveAccessor.java index 0b90a8c973..54ecf92735 100644 --- a/src/main/java/org/springframework/data/redis/core/TimeToLiveAccessor.java +++ b/src/main/java/org/springframework/data/redis/core/TimeToLiveAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/TimeoutUtils.java b/src/main/java/org/springframework/data/redis/core/TimeoutUtils.java index 064fda4a8e..86d90ca882 100644 --- a/src/main/java/org/springframework/data/redis/core/TimeoutUtils.java +++ b/src/main/java/org/springframework/data/redis/core/TimeoutUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ValueOperations.java b/src/main/java/org/springframework/data/redis/core/ValueOperations.java index 7bdf6ad7c6..e3abd0918f 100644 --- a/src/main/java/org/springframework/data/redis/core/ValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ValueOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ValueOperationsEditor.java b/src/main/java/org/springframework/data/redis/core/ValueOperationsEditor.java index 1eaf8949fa..dd87ce60f4 100644 --- a/src/main/java/org/springframework/data/redis/core/ValueOperationsEditor.java +++ b/src/main/java/org/springframework/data/redis/core/ValueOperationsEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ZSetOperations.java b/src/main/java/org/springframework/data/redis/core/ZSetOperations.java index e20f560410..263346fe5f 100644 --- a/src/main/java/org/springframework/data/redis/core/ZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ZSetOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/ZSetOperationsEditor.java b/src/main/java/org/springframework/data/redis/core/ZSetOperationsEditor.java index 124eccfd07..23526dc956 100644 --- a/src/main/java/org/springframework/data/redis/core/ZSetOperationsEditor.java +++ b/src/main/java/org/springframework/data/redis/core/ZSetOperationsEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/BinaryConverters.java b/src/main/java/org/springframework/data/redis/core/convert/BinaryConverters.java index 187c42e807..0befb22a44 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/BinaryConverters.java +++ b/src/main/java/org/springframework/data/redis/core/convert/BinaryConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/Bucket.java b/src/main/java/org/springframework/data/redis/core/convert/Bucket.java index 01be61b6eb..e832acf8e7 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/Bucket.java +++ b/src/main/java/org/springframework/data/redis/core/convert/Bucket.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/CompositeIndexResolver.java b/src/main/java/org/springframework/data/redis/core/convert/CompositeIndexResolver.java index fd6d702cd0..13581c45dc 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/CompositeIndexResolver.java +++ b/src/main/java/org/springframework/data/redis/core/convert/CompositeIndexResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapper.java b/src/main/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapper.java index 5896c6ff43..9b538ec135 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapper.java +++ b/src/main/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/GeoIndexedPropertyValue.java b/src/main/java/org/springframework/data/redis/core/convert/GeoIndexedPropertyValue.java index e45af221bc..d45cd31a16 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/GeoIndexedPropertyValue.java +++ b/src/main/java/org/springframework/data/redis/core/convert/GeoIndexedPropertyValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/IndexResolver.java b/src/main/java/org/springframework/data/redis/core/convert/IndexResolver.java index 95c63c2992..b06e82587a 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/IndexResolver.java +++ b/src/main/java/org/springframework/data/redis/core/convert/IndexResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/IndexedData.java b/src/main/java/org/springframework/data/redis/core/convert/IndexedData.java index 122bdfd2c7..298c48cf6b 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/IndexedData.java +++ b/src/main/java/org/springframework/data/redis/core/convert/IndexedData.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/IndexedDataFactoryProvider.java b/src/main/java/org/springframework/data/redis/core/convert/IndexedDataFactoryProvider.java index c9521d0602..3c4a51bbf7 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/IndexedDataFactoryProvider.java +++ b/src/main/java/org/springframework/data/redis/core/convert/IndexedDataFactoryProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/Jsr310Converters.java b/src/main/java/org/springframework/data/redis/core/convert/Jsr310Converters.java index d4e874e9e9..225e1ef85b 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/Jsr310Converters.java +++ b/src/main/java/org/springframework/data/redis/core/convert/Jsr310Converters.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/KeyspaceConfiguration.java b/src/main/java/org/springframework/data/redis/core/convert/KeyspaceConfiguration.java index d3fcd1b5cb..5bb5b0a470 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/KeyspaceConfiguration.java +++ b/src/main/java/org/springframework/data/redis/core/convert/KeyspaceConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/MappingConfiguration.java b/src/main/java/org/springframework/data/redis/core/convert/MappingConfiguration.java index a938bbeafa..61d79cf68f 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/MappingConfiguration.java +++ b/src/main/java/org/springframework/data/redis/core/convert/MappingConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java b/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java index 813c8c5a69..1cea9a221a 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java +++ b/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/PathIndexResolver.java b/src/main/java/org/springframework/data/redis/core/convert/PathIndexResolver.java index f23873ee9d..00c1964217 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/PathIndexResolver.java +++ b/src/main/java/org/springframework/data/redis/core/convert/PathIndexResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/RedisConverter.java b/src/main/java/org/springframework/data/redis/core/convert/RedisConverter.java index 998f223233..dbbb692472 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/RedisConverter.java +++ b/src/main/java/org/springframework/data/redis/core/convert/RedisConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/RedisCustomConversions.java b/src/main/java/org/springframework/data/redis/core/convert/RedisCustomConversions.java index 5f097242a9..a58f3c9b23 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/RedisCustomConversions.java +++ b/src/main/java/org/springframework/data/redis/core/convert/RedisCustomConversions.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/RedisData.java b/src/main/java/org/springframework/data/redis/core/convert/RedisData.java index a571c205f9..1b68ebaefd 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/RedisData.java +++ b/src/main/java/org/springframework/data/redis/core/convert/RedisData.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/RedisTypeMapper.java b/src/main/java/org/springframework/data/redis/core/convert/RedisTypeMapper.java index 8f8ba965ab..6babc758a4 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/RedisTypeMapper.java +++ b/src/main/java/org/springframework/data/redis/core/convert/RedisTypeMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/ReferenceResolver.java b/src/main/java/org/springframework/data/redis/core/convert/ReferenceResolver.java index a68653200c..0c2bc6b37f 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/ReferenceResolver.java +++ b/src/main/java/org/springframework/data/redis/core/convert/ReferenceResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/ReferenceResolverImpl.java b/src/main/java/org/springframework/data/redis/core/convert/ReferenceResolverImpl.java index 19ad617332..32d5b7f496 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/ReferenceResolverImpl.java +++ b/src/main/java/org/springframework/data/redis/core/convert/ReferenceResolverImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/RemoveIndexedData.java b/src/main/java/org/springframework/data/redis/core/convert/RemoveIndexedData.java index fde0d7d740..d8d5820c96 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/RemoveIndexedData.java +++ b/src/main/java/org/springframework/data/redis/core/convert/RemoveIndexedData.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/SimpleIndexedPropertyValue.java b/src/main/java/org/springframework/data/redis/core/convert/SimpleIndexedPropertyValue.java index f218e7d07a..27d8f21612 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/SimpleIndexedPropertyValue.java +++ b/src/main/java/org/springframework/data/redis/core/convert/SimpleIndexedPropertyValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/convert/SpelIndexResolver.java b/src/main/java/org/springframework/data/redis/core/convert/SpelIndexResolver.java index a386a29cdc..4b5f8418ae 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/SpelIndexResolver.java +++ b/src/main/java/org/springframework/data/redis/core/convert/SpelIndexResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/ConfigurableIndexDefinitionProvider.java b/src/main/java/org/springframework/data/redis/core/index/ConfigurableIndexDefinitionProvider.java index d97a686caa..4c22120359 100644 --- a/src/main/java/org/springframework/data/redis/core/index/ConfigurableIndexDefinitionProvider.java +++ b/src/main/java/org/springframework/data/redis/core/index/ConfigurableIndexDefinitionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/GeoIndexDefinition.java b/src/main/java/org/springframework/data/redis/core/index/GeoIndexDefinition.java index 94157fef78..9c2af6ca81 100644 --- a/src/main/java/org/springframework/data/redis/core/index/GeoIndexDefinition.java +++ b/src/main/java/org/springframework/data/redis/core/index/GeoIndexDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/GeoIndexed.java b/src/main/java/org/springframework/data/redis/core/index/GeoIndexed.java index 185b7bd19d..dd6c353abf 100644 --- a/src/main/java/org/springframework/data/redis/core/index/GeoIndexed.java +++ b/src/main/java/org/springframework/data/redis/core/index/GeoIndexed.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/IndexConfiguration.java b/src/main/java/org/springframework/data/redis/core/index/IndexConfiguration.java index 12b55bbaf5..cb226bbaba 100644 --- a/src/main/java/org/springframework/data/redis/core/index/IndexConfiguration.java +++ b/src/main/java/org/springframework/data/redis/core/index/IndexConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/IndexDefinition.java b/src/main/java/org/springframework/data/redis/core/index/IndexDefinition.java index 86151fc07e..deb6775be8 100644 --- a/src/main/java/org/springframework/data/redis/core/index/IndexDefinition.java +++ b/src/main/java/org/springframework/data/redis/core/index/IndexDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/IndexDefinitionProvider.java b/src/main/java/org/springframework/data/redis/core/index/IndexDefinitionProvider.java index e4d07547d4..df4ee0769d 100644 --- a/src/main/java/org/springframework/data/redis/core/index/IndexDefinitionProvider.java +++ b/src/main/java/org/springframework/data/redis/core/index/IndexDefinitionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/IndexDefinitionRegistry.java b/src/main/java/org/springframework/data/redis/core/index/IndexDefinitionRegistry.java index 8e723ad498..90a395307b 100644 --- a/src/main/java/org/springframework/data/redis/core/index/IndexDefinitionRegistry.java +++ b/src/main/java/org/springframework/data/redis/core/index/IndexDefinitionRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/IndexValueTransformer.java b/src/main/java/org/springframework/data/redis/core/index/IndexValueTransformer.java index 13d77f9ee7..127c3ed88a 100644 --- a/src/main/java/org/springframework/data/redis/core/index/IndexValueTransformer.java +++ b/src/main/java/org/springframework/data/redis/core/index/IndexValueTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/Indexed.java b/src/main/java/org/springframework/data/redis/core/index/Indexed.java index 15dc14d3c0..1099d28265 100644 --- a/src/main/java/org/springframework/data/redis/core/index/Indexed.java +++ b/src/main/java/org/springframework/data/redis/core/index/Indexed.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/PathBasedRedisIndexDefinition.java b/src/main/java/org/springframework/data/redis/core/index/PathBasedRedisIndexDefinition.java index 9526f279b3..3f8e90c883 100644 --- a/src/main/java/org/springframework/data/redis/core/index/PathBasedRedisIndexDefinition.java +++ b/src/main/java/org/springframework/data/redis/core/index/PathBasedRedisIndexDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/RedisIndexDefinition.java b/src/main/java/org/springframework/data/redis/core/index/RedisIndexDefinition.java index 8e0e522fe3..9831e3b9a0 100644 --- a/src/main/java/org/springframework/data/redis/core/index/RedisIndexDefinition.java +++ b/src/main/java/org/springframework/data/redis/core/index/RedisIndexDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/SimpleIndexDefinition.java b/src/main/java/org/springframework/data/redis/core/index/SimpleIndexDefinition.java index 5bd19d501d..75d7ba6163 100644 --- a/src/main/java/org/springframework/data/redis/core/index/SimpleIndexDefinition.java +++ b/src/main/java/org/springframework/data/redis/core/index/SimpleIndexDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/index/SpelIndexDefinition.java b/src/main/java/org/springframework/data/redis/core/index/SpelIndexDefinition.java index 8501e3b1b9..c346945686 100644 --- a/src/main/java/org/springframework/data/redis/core/index/SpelIndexDefinition.java +++ b/src/main/java/org/springframework/data/redis/core/index/SpelIndexDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java b/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java index 5b2f36333b..28eede0b9d 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java b/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java index 0907cab4f2..fa445cbfa5 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java b/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java index 34ba7db186..4ef2104351 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentProperty.java b/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentProperty.java index e607b2aeb7..81a21681df 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentProperty.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/query/DefaultSortCriterion.java b/src/main/java/org/springframework/data/redis/core/query/DefaultSortCriterion.java index af4c532900..5ffecf9c73 100644 --- a/src/main/java/org/springframework/data/redis/core/query/DefaultSortCriterion.java +++ b/src/main/java/org/springframework/data/redis/core/query/DefaultSortCriterion.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/query/DefaultSortQuery.java b/src/main/java/org/springframework/data/redis/core/query/DefaultSortQuery.java index 3746520ce9..62f07e69ee 100644 --- a/src/main/java/org/springframework/data/redis/core/query/DefaultSortQuery.java +++ b/src/main/java/org/springframework/data/redis/core/query/DefaultSortQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/query/QueryUtils.java b/src/main/java/org/springframework/data/redis/core/query/QueryUtils.java index dd11c5dfdd..9aa364f578 100644 --- a/src/main/java/org/springframework/data/redis/core/query/QueryUtils.java +++ b/src/main/java/org/springframework/data/redis/core/query/QueryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/query/SortCriterion.java b/src/main/java/org/springframework/data/redis/core/query/SortCriterion.java index acb86537a1..10d3703149 100644 --- a/src/main/java/org/springframework/data/redis/core/query/SortCriterion.java +++ b/src/main/java/org/springframework/data/redis/core/query/SortCriterion.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/query/SortQuery.java b/src/main/java/org/springframework/data/redis/core/query/SortQuery.java index 491a9eea30..7cedfbc180 100644 --- a/src/main/java/org/springframework/data/redis/core/query/SortQuery.java +++ b/src/main/java/org/springframework/data/redis/core/query/SortQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/query/SortQueryBuilder.java b/src/main/java/org/springframework/data/redis/core/query/SortQueryBuilder.java index 2065256b38..35b739b9b7 100644 --- a/src/main/java/org/springframework/data/redis/core/query/SortQueryBuilder.java +++ b/src/main/java/org/springframework/data/redis/core/query/SortQueryBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutor.java b/src/main/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutor.java index 66c0bb4303..bf1f28a55d 100644 --- a/src/main/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutor.java +++ b/src/main/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/script/DefaultRedisScript.java b/src/main/java/org/springframework/data/redis/core/script/DefaultRedisScript.java index 1d7dac0286..f5e927d6ee 100644 --- a/src/main/java/org/springframework/data/redis/core/script/DefaultRedisScript.java +++ b/src/main/java/org/springframework/data/redis/core/script/DefaultRedisScript.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/script/DefaultScriptExecutor.java b/src/main/java/org/springframework/data/redis/core/script/DefaultScriptExecutor.java index 9c96c1671c..d33af3c5f3 100644 --- a/src/main/java/org/springframework/data/redis/core/script/DefaultScriptExecutor.java +++ b/src/main/java/org/springframework/data/redis/core/script/DefaultScriptExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/script/DigestUtils.java b/src/main/java/org/springframework/data/redis/core/script/DigestUtils.java index 316fa5ea6f..d0bb64a3ca 100644 --- a/src/main/java/org/springframework/data/redis/core/script/DigestUtils.java +++ b/src/main/java/org/springframework/data/redis/core/script/DigestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/script/ReactiveScriptExecutor.java b/src/main/java/org/springframework/data/redis/core/script/ReactiveScriptExecutor.java index 6cd520f394..5824b57d7d 100644 --- a/src/main/java/org/springframework/data/redis/core/script/ReactiveScriptExecutor.java +++ b/src/main/java/org/springframework/data/redis/core/script/ReactiveScriptExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/script/RedisScript.java b/src/main/java/org/springframework/data/redis/core/script/RedisScript.java index 67ec37ed5a..a7cc80f2c2 100644 --- a/src/main/java/org/springframework/data/redis/core/script/RedisScript.java +++ b/src/main/java/org/springframework/data/redis/core/script/RedisScript.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/script/ScriptExecutor.java b/src/main/java/org/springframework/data/redis/core/script/ScriptExecutor.java index 953f27fbb1..ba40f40f0d 100644 --- a/src/main/java/org/springframework/data/redis/core/script/ScriptExecutor.java +++ b/src/main/java/org/springframework/data/redis/core/script/ScriptExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/script/ScriptUtils.java b/src/main/java/org/springframework/data/redis/core/script/ScriptUtils.java index e55dd813b6..24df5ae572 100644 --- a/src/main/java/org/springframework/data/redis/core/script/ScriptUtils.java +++ b/src/main/java/org/springframework/data/redis/core/script/ScriptUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/script/ScriptingException.java b/src/main/java/org/springframework/data/redis/core/script/ScriptingException.java index 09315fb6a7..c0794c828e 100644 --- a/src/main/java/org/springframework/data/redis/core/script/ScriptingException.java +++ b/src/main/java/org/springframework/data/redis/core/script/ScriptingException.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/types/Expiration.java b/src/main/java/org/springframework/data/redis/core/types/Expiration.java index 81930db228..74eb9c3838 100644 --- a/src/main/java/org/springframework/data/redis/core/types/Expiration.java +++ b/src/main/java/org/springframework/data/redis/core/types/Expiration.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java b/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java index 95a511f99d..e480d995a0 100644 --- a/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java +++ b/src/main/java/org/springframework/data/redis/core/types/RedisClientInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java b/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java index b6702bcc24..77e5aa10ef 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/BoundingBox.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/domain/geo/BoxShape.java b/src/main/java/org/springframework/data/redis/domain/geo/BoxShape.java index 00f410fd37..7802726248 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/BoxShape.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/BoxShape.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/domain/geo/GeoLocation.java b/src/main/java/org/springframework/data/redis/domain/geo/GeoLocation.java index ee60b5ea4e..3ed71bdbc5 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/GeoLocation.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/GeoLocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/domain/geo/GeoReference.java b/src/main/java/org/springframework/data/redis/domain/geo/GeoReference.java index 004f4c5c3b..75fe501422 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/GeoReference.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/GeoReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/domain/geo/GeoShape.java b/src/main/java/org/springframework/data/redis/domain/geo/GeoShape.java index c3ddf47818..e324ed41b7 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/GeoShape.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/GeoShape.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/domain/geo/Metrics.java b/src/main/java/org/springframework/data/redis/domain/geo/Metrics.java index f79b5983aa..8c8064a497 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/Metrics.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/Metrics.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/domain/geo/RadiusShape.java b/src/main/java/org/springframework/data/redis/domain/geo/RadiusShape.java index d8b8213e5a..24d8d61b9a 100644 --- a/src/main/java/org/springframework/data/redis/domain/geo/RadiusShape.java +++ b/src/main/java/org/springframework/data/redis/domain/geo/RadiusShape.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/hash/BeanUtilsHashMapper.java b/src/main/java/org/springframework/data/redis/hash/BeanUtilsHashMapper.java index bdb4a29671..98887bfdcb 100644 --- a/src/main/java/org/springframework/data/redis/hash/BeanUtilsHashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/BeanUtilsHashMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/hash/DecoratingStringHashMapper.java b/src/main/java/org/springframework/data/redis/hash/DecoratingStringHashMapper.java index ebbeecfa48..33984c0e01 100644 --- a/src/main/java/org/springframework/data/redis/hash/DecoratingStringHashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/DecoratingStringHashMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/hash/HashMapper.java b/src/main/java/org/springframework/data/redis/hash/HashMapper.java index d7c8e18b72..0ec97f1e89 100644 --- a/src/main/java/org/springframework/data/redis/hash/HashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/HashMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java index ea9c5d24b4..8788c41ba4 100644 --- a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/hash/ObjectHashMapper.java b/src/main/java/org/springframework/data/redis/hash/ObjectHashMapper.java index 10aa244ac5..7a3eb8c94b 100644 --- a/src/main/java/org/springframework/data/redis/hash/ObjectHashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/ObjectHashMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/AbstractTopic.java b/src/main/java/org/springframework/data/redis/listener/AbstractTopic.java index 8cfc1ca3e6..6c233fc043 100644 --- a/src/main/java/org/springframework/data/redis/listener/AbstractTopic.java +++ b/src/main/java/org/springframework/data/redis/listener/AbstractTopic.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/ChannelTopic.java b/src/main/java/org/springframework/data/redis/listener/ChannelTopic.java index 73eb81a24d..f391bd5fcf 100644 --- a/src/main/java/org/springframework/data/redis/listener/ChannelTopic.java +++ b/src/main/java/org/springframework/data/redis/listener/ChannelTopic.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListener.java b/src/main/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListener.java index 7a43963ec8..759d7d33cd 100644 --- a/src/main/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListener.java +++ b/src/main/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/KeyspaceEventMessageListener.java b/src/main/java/org/springframework/data/redis/listener/KeyspaceEventMessageListener.java index eb61014037..1500dd1f1a 100644 --- a/src/main/java/org/springframework/data/redis/listener/KeyspaceEventMessageListener.java +++ b/src/main/java/org/springframework/data/redis/listener/KeyspaceEventMessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/PatternTopic.java b/src/main/java/org/springframework/data/redis/listener/PatternTopic.java index efb6fcb257..57cd7ca8bd 100644 --- a/src/main/java/org/springframework/data/redis/listener/PatternTopic.java +++ b/src/main/java/org/springframework/data/redis/listener/PatternTopic.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainer.java index c23a75b9ed..f58dc7152e 100644 --- a/src/main/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java index b180a6bfd9..a563bdb1ed 100644 --- a/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/SynchronizingMessageListener.java b/src/main/java/org/springframework/data/redis/listener/SynchronizingMessageListener.java index 14210d859c..050e425f3d 100644 --- a/src/main/java/org/springframework/data/redis/listener/SynchronizingMessageListener.java +++ b/src/main/java/org/springframework/data/redis/listener/SynchronizingMessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/Topic.java b/src/main/java/org/springframework/data/redis/listener/Topic.java index 23f9b6f2d3..0aad48207a 100644 --- a/src/main/java/org/springframework/data/redis/listener/Topic.java +++ b/src/main/java/org/springframework/data/redis/listener/Topic.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/adapter/MessageListenerAdapter.java b/src/main/java/org/springframework/data/redis/listener/adapter/MessageListenerAdapter.java index ed9c67ae2b..fb7d827728 100644 --- a/src/main/java/org/springframework/data/redis/listener/adapter/MessageListenerAdapter.java +++ b/src/main/java/org/springframework/data/redis/listener/adapter/MessageListenerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/listener/adapter/RedisListenerExecutionFailedException.java b/src/main/java/org/springframework/data/redis/listener/adapter/RedisListenerExecutionFailedException.java index 8a07b95c36..ff22789d16 100644 --- a/src/main/java/org/springframework/data/redis/listener/adapter/RedisListenerExecutionFailedException.java +++ b/src/main/java/org/springframework/data/redis/listener/adapter/RedisListenerExecutionFailedException.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/cdi/CdiBean.java b/src/main/java/org/springframework/data/redis/repository/cdi/CdiBean.java index 6a12dd3ea6..275ed2fd08 100644 --- a/src/main/java/org/springframework/data/redis/repository/cdi/CdiBean.java +++ b/src/main/java/org/springframework/data/redis/repository/cdi/CdiBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueAdapterBean.java b/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueAdapterBean.java index 5c93d1009c..0220531079 100644 --- a/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueAdapterBean.java +++ b/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueAdapterBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueTemplateBean.java b/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueTemplateBean.java index eb6dc61941..e1f80694be 100644 --- a/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueTemplateBean.java +++ b/src/main/java/org/springframework/data/redis/repository/cdi/RedisKeyValueTemplateBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryBean.java b/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryBean.java index efd211928a..8a85c7bde4 100644 --- a/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryBean.java +++ b/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryExtension.java b/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryExtension.java index 63d88805de..69469551fc 100644 --- a/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryExtension.java +++ b/src/main/java/org/springframework/data/redis/repository/cdi/RedisRepositoryExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/configuration/EnableRedisRepositories.java b/src/main/java/org/springframework/data/redis/repository/configuration/EnableRedisRepositories.java index 54058c38ea..1783584d4e 100644 --- a/src/main/java/org/springframework/data/redis/repository/configuration/EnableRedisRepositories.java +++ b/src/main/java/org/springframework/data/redis/repository/configuration/EnableRedisRepositories.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrar.java b/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrar.java index 21f255f2e6..a681bfb89f 100644 --- a/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrar.java +++ b/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtension.java b/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtension.java index 728cab6d19..5d008d5cd1 100644 --- a/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtension.java +++ b/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformation.java b/src/main/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformation.java index 3af170b234..207ab0f5bc 100644 --- a/src/main/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformation.java +++ b/src/main/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/core/RedisEntityInformation.java b/src/main/java/org/springframework/data/redis/repository/core/RedisEntityInformation.java index 7b6a7b1e71..e2568dad64 100644 --- a/src/main/java/org/springframework/data/redis/repository/core/RedisEntityInformation.java +++ b/src/main/java/org/springframework/data/redis/repository/core/RedisEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/query/ExampleQueryMapper.java b/src/main/java/org/springframework/data/redis/repository/query/ExampleQueryMapper.java index a5b0c76443..9a42260958 100644 --- a/src/main/java/org/springframework/data/redis/repository/query/ExampleQueryMapper.java +++ b/src/main/java/org/springframework/data/redis/repository/query/ExampleQueryMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/query/RedisOperationChain.java b/src/main/java/org/springframework/data/redis/repository/query/RedisOperationChain.java index c8eab2d3a4..e6985cd4db 100644 --- a/src/main/java/org/springframework/data/redis/repository/query/RedisOperationChain.java +++ b/src/main/java/org/springframework/data/redis/repository/query/RedisOperationChain.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/query/RedisPartTreeQuery.java b/src/main/java/org/springframework/data/redis/repository/query/RedisPartTreeQuery.java index 8ba04ac02e..5582f36cc4 100644 --- a/src/main/java/org/springframework/data/redis/repository/query/RedisPartTreeQuery.java +++ b/src/main/java/org/springframework/data/redis/repository/query/RedisPartTreeQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java b/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java index c7541ac8e7..2fbf28d2d0 100644 --- a/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java +++ b/src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutor.java b/src/main/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutor.java index 6a3a9584d2..44704c6096 100644 --- a/src/main/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutor.java +++ b/src/main/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/support/RedisRepositoryFactory.java b/src/main/java/org/springframework/data/redis/repository/support/RedisRepositoryFactory.java index 5197d7665c..d6787357dc 100644 --- a/src/main/java/org/springframework/data/redis/repository/support/RedisRepositoryFactory.java +++ b/src/main/java/org/springframework/data/redis/repository/support/RedisRepositoryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/repository/support/RedisRepositoryFactoryBean.java b/src/main/java/org/springframework/data/redis/repository/support/RedisRepositoryFactoryBean.java index b0ecb53970..37cf0896c5 100644 --- a/src/main/java/org/springframework/data/redis/repository/support/RedisRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/redis/repository/support/RedisRepositoryFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/ByteArrayRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/ByteArrayRedisSerializer.java index 57a2cb9e57..cb24add7a0 100644 --- a/src/main/java/org/springframework/data/redis/serializer/ByteArrayRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/ByteArrayRedisSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementReader.java b/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementReader.java index 0be8d02331..be012f84d0 100644 --- a/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementReader.java +++ b/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementWriter.java b/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementWriter.java index 38bfc19249..e6f75c5f70 100644 --- a/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementWriter.java +++ b/src/main/java/org/springframework/data/redis/serializer/DefaultRedisElementWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/DefaultRedisSerializationContext.java b/src/main/java/org/springframework/data/redis/serializer/DefaultRedisSerializationContext.java index 8232b17db8..b302ea64b9 100644 --- a/src/main/java/org/springframework/data/redis/serializer/DefaultRedisSerializationContext.java +++ b/src/main/java/org/springframework/data/redis/serializer/DefaultRedisSerializationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/DefaultSerializationPair.java b/src/main/java/org/springframework/data/redis/serializer/DefaultSerializationPair.java index 2e4527d503..9f5f9491ef 100644 --- a/src/main/java/org/springframework/data/redis/serializer/DefaultSerializationPair.java +++ b/src/main/java/org/springframework/data/redis/serializer/DefaultSerializationPair.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index f989e27802..35f7f38865 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericToStringSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericToStringSerializer.java index 1071f6ee52..a85d9d8e33 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericToStringSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericToStringSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializer.java index 76238bc60d..daa55032a5 100644 --- a/src/main/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/JacksonObjectReader.java b/src/main/java/org/springframework/data/redis/serializer/JacksonObjectReader.java index 4e76cd4ba7..e2c1d943ec 100644 --- a/src/main/java/org/springframework/data/redis/serializer/JacksonObjectReader.java +++ b/src/main/java/org/springframework/data/redis/serializer/JacksonObjectReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/JacksonObjectWriter.java b/src/main/java/org/springframework/data/redis/serializer/JacksonObjectWriter.java index b7383697f7..88db313130 100644 --- a/src/main/java/org/springframework/data/redis/serializer/JacksonObjectWriter.java +++ b/src/main/java/org/springframework/data/redis/serializer/JacksonObjectWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java index d25ea9c395..9ed493346c 100644 --- a/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/OxmSerializer.java b/src/main/java/org/springframework/data/redis/serializer/OxmSerializer.java index fb1eb4da5e..7d98db1b64 100644 --- a/src/main/java/org/springframework/data/redis/serializer/OxmSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/OxmSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/RedisElementReader.java b/src/main/java/org/springframework/data/redis/serializer/RedisElementReader.java index 445bebf45a..fb2c37dac4 100644 --- a/src/main/java/org/springframework/data/redis/serializer/RedisElementReader.java +++ b/src/main/java/org/springframework/data/redis/serializer/RedisElementReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/RedisElementWriter.java b/src/main/java/org/springframework/data/redis/serializer/RedisElementWriter.java index fe30afbe50..c67f2b0fa8 100644 --- a/src/main/java/org/springframework/data/redis/serializer/RedisElementWriter.java +++ b/src/main/java/org/springframework/data/redis/serializer/RedisElementWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/RedisSerializationContext.java b/src/main/java/org/springframework/data/redis/serializer/RedisSerializationContext.java index f3157b3407..42f9c75639 100644 --- a/src/main/java/org/springframework/data/redis/serializer/RedisSerializationContext.java +++ b/src/main/java/org/springframework/data/redis/serializer/RedisSerializationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/RedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/RedisSerializer.java index 648a69dcd7..5141c5e388 100644 --- a/src/main/java/org/springframework/data/redis/serializer/RedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/RedisSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/RedisSerializerToSerializationPairAdapter.java b/src/main/java/org/springframework/data/redis/serializer/RedisSerializerToSerializationPairAdapter.java index 03ca10377d..fc436d8dd5 100644 --- a/src/main/java/org/springframework/data/redis/serializer/RedisSerializerToSerializationPairAdapter.java +++ b/src/main/java/org/springframework/data/redis/serializer/RedisSerializerToSerializationPairAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/SerializationException.java b/src/main/java/org/springframework/data/redis/serializer/SerializationException.java index 3f03667ff1..1493dd6ef1 100644 --- a/src/main/java/org/springframework/data/redis/serializer/SerializationException.java +++ b/src/main/java/org/springframework/data/redis/serializer/SerializationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/SerializationUtils.java b/src/main/java/org/springframework/data/redis/serializer/SerializationUtils.java index bd2de9dffc..ee78179598 100644 --- a/src/main/java/org/springframework/data/redis/serializer/SerializationUtils.java +++ b/src/main/java/org/springframework/data/redis/serializer/SerializationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java index 8af0002f46..e8e5cd2209 100644 --- a/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/Cancelable.java b/src/main/java/org/springframework/data/redis/stream/Cancelable.java index b7e3cd6369..c1b89a831b 100644 --- a/src/main/java/org/springframework/data/redis/stream/Cancelable.java +++ b/src/main/java/org/springframework/data/redis/stream/Cancelable.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainer.java index bd727ce89c..03c7d6add8 100644 --- a/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/DefaultStreamReceiver.java b/src/main/java/org/springframework/data/redis/stream/DefaultStreamReceiver.java index 7452abfa21..9d9349e18b 100644 --- a/src/main/java/org/springframework/data/redis/stream/DefaultStreamReceiver.java +++ b/src/main/java/org/springframework/data/redis/stream/DefaultStreamReceiver.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/ReadOffsetStrategy.java b/src/main/java/org/springframework/data/redis/stream/ReadOffsetStrategy.java index 45cb9e8cac..2f2cc9561d 100644 --- a/src/main/java/org/springframework/data/redis/stream/ReadOffsetStrategy.java +++ b/src/main/java/org/springframework/data/redis/stream/ReadOffsetStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/StreamListener.java b/src/main/java/org/springframework/data/redis/stream/StreamListener.java index 0c370c47a9..4122a2d625 100644 --- a/src/main/java/org/springframework/data/redis/stream/StreamListener.java +++ b/src/main/java/org/springframework/data/redis/stream/StreamListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java index 0d35c813fe..851a1f6d66 100644 --- a/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/StreamPollTask.java b/src/main/java/org/springframework/data/redis/stream/StreamPollTask.java index 6641a791c3..916c276478 100644 --- a/src/main/java/org/springframework/data/redis/stream/StreamPollTask.java +++ b/src/main/java/org/springframework/data/redis/stream/StreamPollTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/StreamReceiver.java b/src/main/java/org/springframework/data/redis/stream/StreamReceiver.java index 3bb2f22af2..89cccd0ad8 100644 --- a/src/main/java/org/springframework/data/redis/stream/StreamReceiver.java +++ b/src/main/java/org/springframework/data/redis/stream/StreamReceiver.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/Subscription.java b/src/main/java/org/springframework/data/redis/stream/Subscription.java index d10005c25d..01db41a52f 100644 --- a/src/main/java/org/springframework/data/redis/stream/Subscription.java +++ b/src/main/java/org/springframework/data/redis/stream/Subscription.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/stream/Task.java b/src/main/java/org/springframework/data/redis/stream/Task.java index 97cf04117a..9371161ef9 100644 --- a/src/main/java/org/springframework/data/redis/stream/Task.java +++ b/src/main/java/org/springframework/data/redis/stream/Task.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/atomic/CompareAndSet.java b/src/main/java/org/springframework/data/redis/support/atomic/CompareAndSet.java index b361fb7714..d5682c626b 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/CompareAndSet.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/CompareAndSet.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java index 9c1f70df7e..a60d55ad3c 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java index f8e092c0bc..ffdb6ff50d 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java index 597b126278..df7f96035d 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java b/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java index 7f0a26f92b..6c6f14d23d 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java +++ b/src/main/java/org/springframework/data/redis/support/collections/AbstractRedisCollection.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/CollectionUtils.java b/src/main/java/org/springframework/data/redis/support/collections/CollectionUtils.java index e5b901a022..867d1ca3be 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/CollectionUtils.java +++ b/src/main/java/org/springframework/data/redis/support/collections/CollectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java index b2ea1d878e..3de2317313 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java index 93fd9cbed1..9dd0274783 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisSet.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisSet.java index a1370d97a4..385124f185 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisSet.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisSet.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java index d6ea8ee0a5..8d1b5aed25 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisCollection.java b/src/main/java/org/springframework/data/redis/support/collections/RedisCollection.java index 5bcf57199e..1dd7195619 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisCollection.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisCollection.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisCollectionFactoryBean.java b/src/main/java/org/springframework/data/redis/support/collections/RedisCollectionFactoryBean.java index a8e6228e41..b6fc3e35d2 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisCollectionFactoryBean.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisCollectionFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisIterator.java b/src/main/java/org/springframework/data/redis/support/collections/RedisIterator.java index afa0624606..10a43ef176 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisIterator.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisIterator.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisList.java b/src/main/java/org/springframework/data/redis/support/collections/RedisList.java index a55f1f7a03..02fb6b55ab 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisList.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisList.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java index 650114cf1d..a2eb3b8985 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java index 61644f9787..d02d86b6fc 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisSet.java b/src/main/java/org/springframework/data/redis/support/collections/RedisSet.java index 60f0b0c3d1..50e0592300 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisSet.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisSet.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisStore.java b/src/main/java/org/springframework/data/redis/support/collections/RedisStore.java index b931b3f014..81677ab094 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisStore.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java b/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java index a0a8f5657d..63a96de44e 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/support/collections/ReversedRedisListView.java b/src/main/java/org/springframework/data/redis/support/collections/ReversedRedisListView.java index 581a9920d2..89dd3da3f0 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/ReversedRedisListView.java +++ b/src/main/java/org/springframework/data/redis/support/collections/ReversedRedisListView.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/util/ByteUtils.java b/src/main/java/org/springframework/data/redis/util/ByteUtils.java index 93e4e76969..6ae97539ed 100644 --- a/src/main/java/org/springframework/data/redis/util/ByteUtils.java +++ b/src/main/java/org/springframework/data/redis/util/ByteUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/data/redis/util/RedisAssertions.java b/src/main/java/org/springframework/data/redis/util/RedisAssertions.java index a218f762ae..22da3c395d 100644 --- a/src/main/java/org/springframework/data/redis/util/RedisAssertions.java +++ b/src/main/java/org/springframework/data/redis/util/RedisAssertions.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/PartialUpdateExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/PartialUpdateExtensions.kt index c36c8d7710..ced500d4f8 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/PartialUpdateExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/PartialUpdateExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/ReactiveGeoOperationsExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/ReactiveGeoOperationsExtensions.kt index 492af3d9a4..bba2924256 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/ReactiveGeoOperationsExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/ReactiveGeoOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/ReactiveHashOperationsExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/ReactiveHashOperationsExtensions.kt index fe26ed4d66..b7a1594921 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/ReactiveHashOperationsExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/ReactiveHashOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/ReactiveHyperLogLogOperationsExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/ReactiveHyperLogLogOperationsExtensions.kt index ebbf651325..6a4e38036a 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/ReactiveHyperLogLogOperationsExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/ReactiveHyperLogLogOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/ReactiveListOperationsExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/ReactiveListOperationsExtensions.kt index b58a93ebcd..ffd6fee07e 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/ReactiveListOperationsExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/ReactiveListOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensions.kt index 0878ae0a8b..4fef76f849 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/ReactiveSetOperationsExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/ReactiveSetOperationsExtensions.kt index bd1d4db2c4..8594699ec2 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/ReactiveSetOperationsExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/ReactiveSetOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/ReactiveStreamOperationsExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/ReactiveStreamOperationsExtensions.kt index 8bba7683c0..579b7fadd7 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/ReactiveStreamOperationsExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/ReactiveStreamOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/ReactiveValueOperationsExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/ReactiveValueOperationsExtensions.kt index 2bdda370d6..44a4a82922 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/ReactiveValueOperationsExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/ReactiveValueOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/ReactiveZSetOperationsExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/ReactiveZSetOperationsExtensions.kt index dcacae65de..e0679e2834 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/ReactiveZSetOperationsExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/ReactiveZSetOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/org/springframework/data/redis/core/script/RedisScriptExtensions.kt b/src/main/kotlin/org/springframework/data/redis/core/script/RedisScriptExtensions.kt index de0ed8a657..e53512eb74 100644 --- a/src/main/kotlin/org/springframework/data/redis/core/script/RedisScriptExtensions.kt +++ b/src/main/kotlin/org/springframework/data/redis/core/script/RedisScriptExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/Address.java b/src/test/java/org/springframework/data/redis/Address.java index 8628d84ca3..f24a9c6d1d 100644 --- a/src/test/java/org/springframework/data/redis/Address.java +++ b/src/test/java/org/springframework/data/redis/Address.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/ByteBufferObjectFactory.java b/src/test/java/org/springframework/data/redis/ByteBufferObjectFactory.java index b3d5315d29..1a6a249cca 100644 --- a/src/test/java/org/springframework/data/redis/ByteBufferObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/ByteBufferObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/ConnectionFactoryTracker.java b/src/test/java/org/springframework/data/redis/ConnectionFactoryTracker.java index d619e6021c..2210c99ae7 100644 --- a/src/test/java/org/springframework/data/redis/ConnectionFactoryTracker.java +++ b/src/test/java/org/springframework/data/redis/ConnectionFactoryTracker.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/DoubleAsStringObjectFactory.java b/src/test/java/org/springframework/data/redis/DoubleAsStringObjectFactory.java index 99d5ddea98..9cd21baef5 100644 --- a/src/test/java/org/springframework/data/redis/DoubleAsStringObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/DoubleAsStringObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/DoubleObjectFactory.java b/src/test/java/org/springframework/data/redis/DoubleObjectFactory.java index 2d2ab66c57..279437ea72 100644 --- a/src/test/java/org/springframework/data/redis/DoubleObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/DoubleObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/LongAsStringObjectFactory.java b/src/test/java/org/springframework/data/redis/LongAsStringObjectFactory.java index 9ee9af93c8..fc183fafdf 100644 --- a/src/test/java/org/springframework/data/redis/LongAsStringObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/LongAsStringObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/LongObjectFactory.java b/src/test/java/org/springframework/data/redis/LongObjectFactory.java index da538b0f6d..773fcad57b 100644 --- a/src/test/java/org/springframework/data/redis/LongObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/LongObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/ObjectFactory.java b/src/test/java/org/springframework/data/redis/ObjectFactory.java index 3438d57ffa..44d0343b34 100644 --- a/src/test/java/org/springframework/data/redis/ObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/ObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/Person.java b/src/test/java/org/springframework/data/redis/Person.java index 0201e14a59..610257d7a0 100644 --- a/src/test/java/org/springframework/data/redis/Person.java +++ b/src/test/java/org/springframework/data/redis/Person.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/PersonObjectFactory.java b/src/test/java/org/springframework/data/redis/PersonObjectFactory.java index d429823a4c..8b5ccb1523 100644 --- a/src/test/java/org/springframework/data/redis/PersonObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/PersonObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/PrefixStringObjectFactory.java b/src/test/java/org/springframework/data/redis/PrefixStringObjectFactory.java index d4b4b34525..c3c5da29c9 100644 --- a/src/test/java/org/springframework/data/redis/PrefixStringObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/PrefixStringObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/PropertyEditorsIntegrationTests.java b/src/test/java/org/springframework/data/redis/PropertyEditorsIntegrationTests.java index 7eae9bdc85..733d742695 100644 --- a/src/test/java/org/springframework/data/redis/PropertyEditorsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/PropertyEditorsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/RawObjectFactory.java b/src/test/java/org/springframework/data/redis/RawObjectFactory.java index fd0148881f..9d1608f17b 100644 --- a/src/test/java/org/springframework/data/redis/RawObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/RawObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/RedisViewPE.java b/src/test/java/org/springframework/data/redis/RedisViewPE.java index d4b7e25853..65f4ffbc6b 100644 --- a/src/test/java/org/springframework/data/redis/RedisViewPE.java +++ b/src/test/java/org/springframework/data/redis/RedisViewPE.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/SettingsUtils.java b/src/test/java/org/springframework/data/redis/SettingsUtils.java index 107c61889f..19548d72a0 100644 --- a/src/test/java/org/springframework/data/redis/SettingsUtils.java +++ b/src/test/java/org/springframework/data/redis/SettingsUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/StringObjectFactory.java b/src/test/java/org/springframework/data/redis/StringObjectFactory.java index 609d9405e9..37d0ae3e12 100644 --- a/src/test/java/org/springframework/data/redis/StringObjectFactory.java +++ b/src/test/java/org/springframework/data/redis/StringObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/TestCondition.java b/src/test/java/org/springframework/data/redis/TestCondition.java index 3eceff67d4..6b17ea91b5 100644 --- a/src/test/java/org/springframework/data/redis/TestCondition.java +++ b/src/test/java/org/springframework/data/redis/TestCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/CacheTestParams.java b/src/test/java/org/springframework/data/redis/cache/CacheTestParams.java index 538a059d33..b037099902 100644 --- a/src/test/java/org/springframework/data/redis/cache/CacheTestParams.java +++ b/src/test/java/org/springframework/data/redis/cache/CacheTestParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/DefaultCacheStatisticsCollectorUnitTests.java b/src/test/java/org/springframework/data/redis/cache/DefaultCacheStatisticsCollectorUnitTests.java index bdef135de1..ae6fcafab1 100644 --- a/src/test/java/org/springframework/data/redis/cache/DefaultCacheStatisticsCollectorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/cache/DefaultCacheStatisticsCollectorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java index d05d5bd6b7..e4b0696a34 100644 --- a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java index 4d6c2cd0fe..7df9bf8652 100644 --- a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java +++ b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/LegacyRedisCacheTests.java b/src/test/java/org/springframework/data/redis/cache/LegacyRedisCacheTests.java index 9ef7231ae7..d718055ffa 100644 --- a/src/test/java/org/springframework/data/redis/cache/LegacyRedisCacheTests.java +++ b/src/test/java/org/springframework/data/redis/cache/LegacyRedisCacheTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/MutableCacheStatisticsUnitTests.java b/src/test/java/org/springframework/data/redis/cache/MutableCacheStatisticsUnitTests.java index b80358ea9e..29c9e1002f 100644 --- a/src/test/java/org/springframework/data/redis/cache/MutableCacheStatisticsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/cache/MutableCacheStatisticsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheConfigurationUnitTests.java index edbe99e964..94de42115d 100644 --- a/src/test/java/org/springframework/data/redis/cache/RedisCacheConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheConfigurationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheManagerUnitTests.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheManagerUnitTests.java index 699d07917d..e03714beb2 100644 --- a/src/test/java/org/springframework/data/redis/cache/RedisCacheManagerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheManagerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java index f5face7248..520b73d09f 100644 --- a/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java +++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheUnitTests.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheUnitTests.java index 94fae0870f..b1b0ef311f 100644 --- a/src/test/java/org/springframework/data/redis/cache/RedisCacheUnitTests.java +++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheWriterUnitTests.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheWriterUnitTests.java index e9d6ce75ff..ec8e69b52c 100644 --- a/src/test/java/org/springframework/data/redis/cache/RedisCacheWriterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheWriterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/config/NamespaceIntegrationTests.java b/src/test/java/org/springframework/data/redis/config/NamespaceIntegrationTests.java index c02fe6be4b..4334ce1028 100644 --- a/src/test/java/org/springframework/data/redis/config/NamespaceIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/config/NamespaceIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/config/PropertyEditorSupportIntegrationTests.java b/src/test/java/org/springframework/data/redis/config/PropertyEditorSupportIntegrationTests.java index 2c88f2a3b3..9ccac8b555 100644 --- a/src/test/java/org/springframework/data/redis/config/PropertyEditorSupportIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/config/PropertyEditorSupportIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/config/StubErrorHandler.java b/src/test/java/org/springframework/data/redis/config/StubErrorHandler.java index 2c43190c89..93b6ea9455 100644 --- a/src/test/java/org/springframework/data/redis/config/StubErrorHandler.java +++ b/src/test/java/org/springframework/data/redis/config/StubErrorHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index b6b9cd6ed4..4662437afe 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionPipelineIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionPipelineIntegrationTests.java index f30dc5f108..f1205d2848 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionPipelineIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionPipelineIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionTransactionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionTransactionIntegrationTests.java index 1ece6b7ed7..52e8836655 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionTransactionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionTransactionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionUnitTestBase.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionUnitTestBase.java index 820f0c4c0d..40c3b9fa88 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionUnitTestBase.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionUnitTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractTransactionalTestBase.java b/src/test/java/org/springframework/data/redis/connection/AbstractTransactionalTestBase.java index 1c63fbdd3e..f99df546be 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractTransactionalTestBase.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractTransactionalTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/BitFieldSubCommandsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/BitFieldSubCommandsUnitTests.java index 693ef624bc..c7876e4092 100644 --- a/src/test/java/org/springframework/data/redis/connection/BitFieldSubCommandsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/BitFieldSubCommandsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/ClusterCommandExecutorUnitTests.java b/src/test/java/org/springframework/data/redis/connection/ClusterCommandExecutorUnitTests.java index b3a6d5f33e..f57a2f5233 100644 --- a/src/test/java/org/springframework/data/redis/connection/ClusterCommandExecutorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ClusterCommandExecutorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java index 2d13df6131..ad64444cc1 100644 --- a/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/ClusterSlotHashUtilsTests.java b/src/test/java/org/springframework/data/redis/connection/ClusterSlotHashUtilsTests.java index ed0f1ef8dc..fa2b4706b5 100644 --- a/src/test/java/org/springframework/data/redis/connection/ClusterSlotHashUtilsTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ClusterSlotHashUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/ClusterTestVariables.java b/src/test/java/org/springframework/data/redis/connection/ClusterTestVariables.java index cb33e10de2..4be52a6363 100644 --- a/src/test/java/org/springframework/data/redis/connection/ClusterTestVariables.java +++ b/src/test/java/org/springframework/data/redis/connection/ClusterTestVariables.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java index 74ad0677e9..40c28082e1 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java index 3f9f4a0cbb..e7a0e6c607 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java index 9931ac330d..4efcf03168 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java index 5f7db6e388..3441a8f581 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java index 9912b92b3c..dedaf0c84d 100644 --- a/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ReactiveStreamCommandsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisClusterConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisClusterConfigurationUnitTests.java index fbd0d24da9..ffc3bde6e8 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisClusterConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisClusterConfigurationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisClusterNodeSlotRangeUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisClusterNodeSlotRangeUnitTests.java index 0345e8bfb5..034a06f38c 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisClusterNodeSlotRangeUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisClusterNodeSlotRangeUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java index 2a10866752..d66c7057ce 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisElastiCacheConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisElastiCacheConfigurationUnitTests.java index d0940d99c6..8d64a7bbbf 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisElastiCacheConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisElastiCacheConfigurationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java index 839f1132a5..0cef0df5ed 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisNodeUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisPasswordUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisPasswordUnitTests.java index ac5c87748f..78f8c7df6b 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisPasswordUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisPasswordUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java index a8c42cb78f..9f9adb4e30 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisServerUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisServerUnitTests.java index 215f56f374..f8ebab1183 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisServerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisServerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java index e8daac5071..74579d57a6 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisStreamCommandsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/RedisZSetCommandsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisZSetCommandsUnitTests.java index 28774e5156..9ebf89e6ea 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisZSetCommandsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisZSetCommandsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/ReturnTypeUnitTests.java b/src/test/java/org/springframework/data/redis/connection/ReturnTypeUnitTests.java index 4c51047f9a..a455115c34 100644 --- a/src/test/java/org/springframework/data/redis/connection/ReturnTypeUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ReturnTypeUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/StreamRecordsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/StreamRecordsUnitTests.java index d1073dc31b..02ab8faf5a 100644 --- a/src/test/java/org/springframework/data/redis/connection/StreamRecordsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/StreamRecordsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/WeightsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/WeightsUnitTests.java index beabaad473..adc1b8e7bd 100644 --- a/src/test/java/org/springframework/data/redis/connection/WeightsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/WeightsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java b/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java index 6bc5938be3..6c7c248558 100644 --- a/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/convert/ConvertersUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisAclIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisAclIntegrationTests.java index 38dd34c0b2..ade77dd515 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisAclIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisAclIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClientConfigurationUnitTests.java index ccb41b5ff9..74203c8bc1 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClientConfigurationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 8ec56d8b36..6fe6eb43c1 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryIntegrationTests.java index 7a341018a7..161561415e 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactorySentinelIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactorySentinelIntegrationTests.java index 1a3a3d6bd1..6a75f265b1 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactorySentinelIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactorySentinelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java index b553cf4415..9b1496c719 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionIntegrationTests.java index 119b507663..b4aae693c2 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java index cbd0edae68..2e34011f4f 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionPipelineIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java index ba43a4fd2b..7e08465475 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionTransactionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionUnitTests.java index 4a9150d4de..348576b710 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConvertersUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConvertersUnitTests.java index 21c1b8e41d..5a869da105 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConvertersUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConvertersUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisExceptionConverterUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisExceptionConverterUnitTests.java index b83593891b..92019da91e 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisExceptionConverterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisExceptionConverterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisSentinelConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisSentinelConnectionUnitTests.java index e7ec289152..1289e7f6dd 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisSentinelConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisSentinelConnectionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisSentinelIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisSentinelIntegrationTests.java index cd036204ae..06150da2d4 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisSentinelIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisSentinelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisSubscriptionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisSubscriptionUnitTests.java index 262c185d59..34b249fe79 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisSubscriptionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisSubscriptionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisTransactionalConnectionStarvationTest.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisTransactionalConnectionStarvationTest.java index fb4e4057fe..0e2d2e9061 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisTransactionalConnectionStarvationTest.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisTransactionalConnectionStarvationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/ScanTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/ScanTests.java index 269cf395a5..c89882bd49 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/ScanTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/ScanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisIntegrationTests.java index e5c79be848..437976e41b 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/TransactionalJedisIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/extension/JedisConnectionFactoryExtension.java b/src/test/java/org/springframework/data/redis/connection/jedis/extension/JedisConnectionFactoryExtension.java index 07b59cd38a..267e5cb37b 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/extension/JedisConnectionFactoryExtension.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/extension/JedisConnectionFactoryExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceAclIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceAclIntegrationTests.java index f16a64f6e5..3d165c856f 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceAclIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceAclIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java index 288aa48151..fbfe069704 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java index 5ecba15478..b1fe07dae6 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionUnitTests.java index 25fd46d783..7d2a1003bf 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterKeyspaceNotificationsTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterKeyspaceNotificationsTests.java index d347283a8b..137423dd57 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterKeyspaceNotificationsTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterKeyspaceNotificationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceCommandArgsComparator.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceCommandArgsComparator.java index 94cacb157f..c3d3997514 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceCommandArgsComparator.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceCommandArgsComparator.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java index 81a811a034..0c2d1d65e2 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java index 3242dacf73..8fd3f0c747 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionIntegrationTests.java index 7ca1190049..afe2108296 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineFlushOnEndIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineFlushOnEndIntegrationTests.java index 1e4133d154..c810a396a1 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineFlushOnEndIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineFlushOnEndIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineIntegrationTests.java index c9e702d2c0..9793364915 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineTxFlushOnEndIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineTxFlushOnEndIntegrationTests.java index e5e49d6a8b..fa4004078a 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineTxFlushOnEndIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineTxFlushOnEndIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineTxIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineTxIntegrationTests.java index 0ad92314da..2d965cbfdf 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineTxIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionPipelineTxIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionTransactionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionTransactionIntegrationTests.java index 0b6f1653ef..f5e153c8dc 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionTransactionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionTransactionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java index 5dd0eb13c1..0ba0123eac 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConvertersUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConvertersUnitTests.java index 0e4c15fffb..74cb969dc6 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConvertersUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConvertersUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java index ba9fd4a31a..55f06d19b2 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfigurationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java index ebd84f545e..bc18a584cc 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterCommandsIntegrationTests.java index 5c439889b6..d069de8231 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHyperLogLogCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHyperLogLogCommandsIntegrationTests.java index 3d94e4a394..70c1932b3e 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHyperLogLogCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterHyperLogLogCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterKeyCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterKeyCommandsIntegrationTests.java index 4d08c1402d..aa7c7fee12 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterKeyCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterKeyCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterListCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterListCommandsIntegrationTests.java index 6632a1f97c..f2afb32b4e 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterListCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterListCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterServerCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterServerCommandsIntegrationTests.java index 197a2b53a1..bb692e97fe 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterServerCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterServerCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommandsIntegrationTests.java index 13848ee84c..1f56137cf2 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterTestSupport.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterTestSupport.java index ec079c5ec6..3e900e339f 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterTestSupport.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterTestSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterZSetCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterZSetCommandsIntegrationTests.java index 0bedf8273f..17684f0dce 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterZSetCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterZSetCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestSupport.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestSupport.java index d0a74f4586..3407fea953 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestSupport.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveGeoCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveGeoCommandsIntegrationTests.java index f01cd12658..5f3d43e2b8 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveGeoCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveGeoCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java index e152cf6311..973e061f90 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHyperLogLogCommandsTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHyperLogLogCommandsTests.java index cdd9e99d8c..2b33e87a38 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHyperLogLogCommandsTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHyperLogLogCommandsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java index 457c009a60..3376c2727f 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommandIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommandIntegrationTests.java index 856466cfc0..b09aa7fe5c 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommandIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommandIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveNumberCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveNumberCommandsIntegrationTests.java index cf072ff594..0b80066cfc 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveNumberCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveNumberCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommandsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommandsUnitTests.java index c4013494ce..17d976e7ac 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommandsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactivePubSubCommandsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisClusterConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisClusterConnectionUnitTests.java index 4c4b6b7f7e..af050574b9 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisClusterConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisClusterConnectionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisConnectionUnitTests.java index e8fe23e141..7e56c13cdb 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveRedisConnectionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommandsIntegrationTests.java index 0a16832f67..17114f4405 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommandsIntegrationTests.java index 09be137a6d..ce6a8ebefe 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveServerCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSetCommandsIntegrationIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSetCommandsIntegrationIntegrationTests.java index 394cb5cba9..f8046aef2c 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSetCommandsIntegrationIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSetCommandsIntegrationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommandsIntegrationTests.java index 9fb4d8401b..b181ef6a60 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java index 15548cfc22..af3e43e135 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSubscriptionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSubscriptionUnitTests.java index 9583d1f760..61f4daf71a 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSubscriptionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSubscriptionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommandsIntegrationTests.java index 78b69f568d..1dac69219b 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommandsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelConnectionUnitTests.java index 3785f6fdcb..4dd4e31b5b 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelConnectionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelIntegrationTests.java index 7c119c3d03..cb57462908 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSentinelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSubscriptionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSubscriptionUnitTests.java index 4c1ba0f9ec..bec4c050cb 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSubscriptionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceSubscriptionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceTestClientConfiguration.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceTestClientConfiguration.java index 086ccf86b0..04c295000e 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceTestClientConfiguration.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceTestClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/PipeliningFlushPolicyUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/PipeliningFlushPolicyUnitTests.java index 00ab6acf52..1dedf516dd 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/PipeliningFlushPolicyUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/PipeliningFlushPolicyUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProviderIntegrationTest.java b/src/test/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProviderIntegrationTest.java index c585c5d3ee..07f17de6aa 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProviderIntegrationTest.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/StaticMasterReplicaConnectionProviderIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/TransactionalLettuceIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/TransactionalLettuceIntegrationTests.java index 9e5f14a6ce..c3a55b3539 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/TransactionalLettuceIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/TransactionalLettuceIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/extension/LettuceConnectionFactoryExtension.java b/src/test/java/org/springframework/data/redis/connection/lettuce/extension/LettuceConnectionFactoryExtension.java index 791c6b312e..e646e12593 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/extension/LettuceConnectionFactoryExtension.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/extension/LettuceConnectionFactoryExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/ReactiveIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/observability/ReactiveIntegrationTests.java index cdbaf8661a..5c352df982 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/ReactiveIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/observability/ReactiveIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/SynchronousIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/observability/SynchronousIntegrationTests.java index 52bbd0b03f..5220a64fd4 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/SynchronousIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/observability/SynchronousIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/TestConfig.java b/src/test/java/org/springframework/data/redis/connection/lettuce/observability/TestConfig.java index af6c481cad..387f799302 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/TestConfig.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/observability/TestConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/connection/stream/StreamReadOptionsUnitTests.java b/src/test/java/org/springframework/data/redis/connection/stream/StreamReadOptionsUnitTests.java index 4ee7bc9dfb..78fa69b066 100644 --- a/src/test/java/org/springframework/data/redis/connection/stream/StreamReadOptionsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/stream/StreamReadOptionsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/AbstractOperationsTestParams.java b/src/test/java/org/springframework/data/redis/core/AbstractOperationsTestParams.java index 4fdc618f8a..485b6b1414 100644 --- a/src/test/java/org/springframework/data/redis/core/AbstractOperationsTestParams.java +++ b/src/test/java/org/springframework/data/redis/core/AbstractOperationsTestParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/BoundOperationsProxyFactoryRuntimeHintTests.java b/src/test/java/org/springframework/data/redis/core/BoundOperationsProxyFactoryRuntimeHintTests.java index c7d763a449..ffd9e4f772 100644 --- a/src/test/java/org/springframework/data/redis/core/BoundOperationsProxyFactoryRuntimeHintTests.java +++ b/src/test/java/org/springframework/data/redis/core/BoundOperationsProxyFactoryRuntimeHintTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/BoundOperationsProxyFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/core/BoundOperationsProxyFactoryUnitTests.java index 5b98034fbd..86e53f174a 100644 --- a/src/test/java/org/springframework/data/redis/core/BoundOperationsProxyFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/BoundOperationsProxyFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/ConnectionMockingRedisTemplate.java b/src/test/java/org/springframework/data/redis/core/ConnectionMockingRedisTemplate.java index af5f87b504..c156421f6a 100644 --- a/src/test/java/org/springframework/data/redis/core/ConnectionMockingRedisTemplate.java +++ b/src/test/java/org/springframework/data/redis/core/ConnectionMockingRedisTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/ConnectionSplittingInterceptorUnitTests.java b/src/test/java/org/springframework/data/redis/core/ConnectionSplittingInterceptorUnitTests.java index c8b83d3a07..c0796cd83b 100644 --- a/src/test/java/org/springframework/data/redis/core/ConnectionSplittingInterceptorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/ConnectionSplittingInterceptorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/ConvertingCursorUnitTests.java b/src/test/java/org/springframework/data/redis/core/ConvertingCursorUnitTests.java index a228f7b589..fb0e9a5515 100644 --- a/src/test/java/org/springframework/data/redis/core/ConvertingCursorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/ConvertingCursorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultClusterOperationsUnitTests.java b/src/test/java/org/springframework/data/redis/core/DefaultClusterOperationsUnitTests.java index e2deedc846..643b01ffe6 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultClusterOperationsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultClusterOperationsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultGeoOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultGeoOperationsIntegrationTests.java index d299c8378f..0000db4885 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultGeoOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultGeoOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java index fe79d8d014..8a2b7065ad 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultHyperLogLogOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultHyperLogLogOperationsIntegrationTests.java index 3ac0a643d4..be31c3932e 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultHyperLogLogOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultHyperLogLogOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java index 7ee0b5d403..4965b45032 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveGeoOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveGeoOperationsIntegrationTests.java index cd9411b4ef..795e1a16af 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveGeoOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveGeoOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java index d844370bfb..dd1e1287ef 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHyperLogLogOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHyperLogLogOperationsIntegrationTests.java index a52d2e20d3..183704a76e 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHyperLogLogOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHyperLogLogOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java index 34eaf7cb5e..f42aaa9af4 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveSetOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveSetOperationsIntegrationTests.java index 6ffeb10725..37b497f893 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveSetOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveSetOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java index 3e3c7d504d..f8e5360e49 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveStreamOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveValueOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveValueOperationsIntegrationTests.java index b1f44caf21..7dfeb0266d 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveValueOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveValueOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java index 6512faaae8..d0e1afa11a 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultSetOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultSetOperationsIntegrationTests.java index d4bf27f48b..67e31a2163 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultSetOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultSetOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java index 9aa146d9ca..6dc4f8dc3d 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultStreamOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultTypedTupleUnitTests.java b/src/test/java/org/springframework/data/redis/core/DefaultTypedTupleUnitTests.java index fa8beb2b52..f0ae042f65 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultTypedTupleUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultTypedTupleUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsIntegrationTests.java index e539412d5a..1d73298e8d 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsUnitTests.java b/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsUnitTests.java index 1a84eb956d..373542bd50 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java index 853b0d698f..dcfd41feb4 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsUnitTests.java b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsUnitTests.java index 02462003c2..fdbb55b91e 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/IndexWriterUnitTests.java b/src/test/java/org/springframework/data/redis/core/IndexWriterUnitTests.java index b1d41586a1..bf7a398c1e 100644 --- a/src/test/java/org/springframework/data/redis/core/IndexWriterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/IndexWriterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java b/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java index ebb7aa53ab..c3df6384aa 100644 --- a/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java +++ b/src/test/java/org/springframework/data/redis/core/MappingExpirationListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/MultithreadedRedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/MultithreadedRedisTemplateIntegrationTests.java index 7981efec59..c0ad3a2893 100644 --- a/src/test/java/org/springframework/data/redis/core/MultithreadedRedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/MultithreadedRedisTemplateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveOperationsTestParams.java b/src/test/java/org/springframework/data/redis/core/ReactiveOperationsTestParams.java index 8364be9311..77895e3bb2 100644 --- a/src/test/java/org/springframework/data/redis/core/ReactiveOperationsTestParams.java +++ b/src/test/java/org/springframework/data/redis/core/ReactiveOperationsTestParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java index 37fabc7b30..bd8eb0b141 100644 --- a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateUnitTests.java b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateUnitTests.java index f27299b272..de6deaa4a0 100644 --- a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveStringRedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/ReactiveStringRedisTemplateIntegrationTests.java index b41ee3dc4e..ea3ec41dd8 100644 --- a/src/test/java/org/springframework/data/redis/core/ReactiveStringRedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/ReactiveStringRedisTemplateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisAccessorUnitTests.java b/src/test/java/org/springframework/data/redis/core/RedisAccessorUnitTests.java index d1f9f4d800..63f7616908 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisAccessorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisAccessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisClusterTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/RedisClusterTemplateIntegrationTests.java index 4a17444966..c2b5f55b01 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisClusterTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisClusterTemplateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisCommandUnitTests.java b/src/test/java/org/springframework/data/redis/core/RedisCommandUnitTests.java index 92c4ab693e..e4d376de82 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisCommandUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisCommandUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisConnectionUtilsUnitTests.java b/src/test/java/org/springframework/data/redis/core/RedisConnectionUtilsUnitTests.java index 19854032ff..2364624d06 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisConnectionUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisConnectionUtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisKeyExpiredEventUnitTests.java b/src/test/java/org/springframework/data/redis/core/RedisKeyExpiredEventUnitTests.java index 6ab8ab530b..4d28d2632b 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisKeyExpiredEventUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisKeyExpiredEventUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java index 94ccf69b29..a445a6bb17 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterUnitTests.java b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterUnitTests.java index dca24e7482..4212f79642 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisKeyValueTemplateTests.java b/src/test/java/org/springframework/data/redis/core/RedisKeyValueTemplateTests.java index ab19b6522c..fb464d61a3 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisKeyValueTemplateTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisKeyValueTemplateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java index bbc18c6f0e..de22a409c4 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/RedisTemplateUnitTests.java b/src/test/java/org/springframework/data/redis/core/RedisTemplateUnitTests.java index 9b22293cf6..06bea63fdc 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisTemplateUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisTemplateUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/ScanCursorUnitTests.java b/src/test/java/org/springframework/data/redis/core/ScanCursorUnitTests.java index bcbecdea3a..c9d8863791 100644 --- a/src/test/java/org/springframework/data/redis/core/ScanCursorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/ScanCursorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/SessionUnitTests.java b/src/test/java/org/springframework/data/redis/core/SessionUnitTests.java index 7b87af8ff0..20ebdd5890 100644 --- a/src/test/java/org/springframework/data/redis/core/SessionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/SessionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/TimeoutUtilsUnitTests.java b/src/test/java/org/springframework/data/redis/core/TimeoutUtilsUnitTests.java index 91ec24027f..ae35baad70 100644 --- a/src/test/java/org/springframework/data/redis/core/TimeoutUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/TimeoutUtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/convert/BinaryKeyspaceIdentifierUnitTests.java b/src/test/java/org/springframework/data/redis/core/convert/BinaryKeyspaceIdentifierUnitTests.java index 99723e9229..eddea1020d 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/BinaryKeyspaceIdentifierUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/convert/BinaryKeyspaceIdentifierUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/convert/CompositeIndexResolverUnitTests.java b/src/test/java/org/springframework/data/redis/core/convert/CompositeIndexResolverUnitTests.java index d859bee0d1..58063cb23d 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/CompositeIndexResolverUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/convert/CompositeIndexResolverUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/convert/ConversionTestEntities.java b/src/test/java/org/springframework/data/redis/core/convert/ConversionTestEntities.java index 41ce475264..c9b5d00c66 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/ConversionTestEntities.java +++ b/src/test/java/org/springframework/data/redis/core/convert/ConversionTestEntities.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapperUnitTests.java b/src/test/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapperUnitTests.java index b1c6b6d2cf..a25465545d 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapperUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/convert/DefaultRedisTypeMapperUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/convert/Jsr310ConvertersTest.java b/src/test/java/org/springframework/data/redis/core/convert/Jsr310ConvertersTest.java index a95ca213ee..9b06715d80 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/Jsr310ConvertersTest.java +++ b/src/test/java/org/springframework/data/redis/core/convert/Jsr310ConvertersTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/convert/KeyspaceIdentifierUnitTests.java b/src/test/java/org/springframework/data/redis/core/convert/KeyspaceIdentifierUnitTests.java index f115d1eb4e..1f965b71dd 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/KeyspaceIdentifierUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/convert/KeyspaceIdentifierUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/convert/MappingRedisConverterUnitTests.java b/src/test/java/org/springframework/data/redis/core/convert/MappingRedisConverterUnitTests.java index aff41be615..c70b933f07 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/MappingRedisConverterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/convert/MappingRedisConverterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/convert/PathIndexResolverUnitTests.java b/src/test/java/org/springframework/data/redis/core/convert/PathIndexResolverUnitTests.java index 8fa29d33c0..8a61c7a41c 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/PathIndexResolverUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/convert/PathIndexResolverUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/convert/SpelIndexResolverUnitTests.java b/src/test/java/org/springframework/data/redis/core/convert/SpelIndexResolverUnitTests.java index 46c11631a3..153b3c9b65 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/SpelIndexResolverUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/convert/SpelIndexResolverUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/index/IndexConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/core/index/IndexConfigurationUnitTests.java index 9201e0b7cb..75c5d8a85e 100644 --- a/src/test/java/org/springframework/data/redis/core/index/IndexConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/index/IndexConfigurationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntityUnitTests.java b/src/test/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntityUnitTests.java index f2d653210a..a353d50573 100644 --- a/src/test/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntityUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/mapping/BasicRedisPersistentEntityUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/mapping/ConfigAwareKeySpaceResolverUnitTests.java b/src/test/java/org/springframework/data/redis/core/mapping/ConfigAwareKeySpaceResolverUnitTests.java index bf3e1d2ef7..182e73214d 100644 --- a/src/test/java/org/springframework/data/redis/core/mapping/ConfigAwareKeySpaceResolverUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/mapping/ConfigAwareKeySpaceResolverUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/mapping/ConfigAwareTimeToLiveAccessorUnitTests.java b/src/test/java/org/springframework/data/redis/core/mapping/ConfigAwareTimeToLiveAccessorUnitTests.java index 9d78ac4a69..29f8804012 100644 --- a/src/test/java/org/springframework/data/redis/core/mapping/ConfigAwareTimeToLiveAccessorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/mapping/ConfigAwareTimeToLiveAccessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/script/AbstractDefaultScriptExecutorTests.java b/src/test/java/org/springframework/data/redis/core/script/AbstractDefaultScriptExecutorTests.java index e6c8b3ce36..005212248f 100644 --- a/src/test/java/org/springframework/data/redis/core/script/AbstractDefaultScriptExecutorTests.java +++ b/src/test/java/org/springframework/data/redis/core/script/AbstractDefaultScriptExecutorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutorTests.java b/src/test/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutorTests.java index 7990d1a145..3676c27c24 100644 --- a/src/test/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutorTests.java +++ b/src/test/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutorUnitTests.java b/src/test/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutorUnitTests.java index 2c68eca8d7..456eed04ce 100644 --- a/src/test/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/script/DefaultReactiveScriptExecutorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/script/DefaultRedisScriptTests.java b/src/test/java/org/springframework/data/redis/core/script/DefaultRedisScriptTests.java index a8b7c14797..3cb6445d39 100644 --- a/src/test/java/org/springframework/data/redis/core/script/DefaultRedisScriptTests.java +++ b/src/test/java/org/springframework/data/redis/core/script/DefaultRedisScriptTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/script/DefaultScriptExecutorUnitTests.java b/src/test/java/org/springframework/data/redis/core/script/DefaultScriptExecutorUnitTests.java index 40283a20f4..82a288fd8d 100644 --- a/src/test/java/org/springframework/data/redis/core/script/DefaultScriptExecutorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/script/DefaultScriptExecutorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/script/jedis/JedisDefaultScriptExecutorTests.java b/src/test/java/org/springframework/data/redis/core/script/jedis/JedisDefaultScriptExecutorTests.java index c9ba2e0eea..6488a36e02 100644 --- a/src/test/java/org/springframework/data/redis/core/script/jedis/JedisDefaultScriptExecutorTests.java +++ b/src/test/java/org/springframework/data/redis/core/script/jedis/JedisDefaultScriptExecutorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/script/lettuce/LettuceDefaultScriptExecutorTests.java b/src/test/java/org/springframework/data/redis/core/script/lettuce/LettuceDefaultScriptExecutorTests.java index a269a87203..8674f49458 100644 --- a/src/test/java/org/springframework/data/redis/core/script/lettuce/LettuceDefaultScriptExecutorTests.java +++ b/src/test/java/org/springframework/data/redis/core/script/lettuce/LettuceDefaultScriptExecutorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/types/ExpirationUnitTests.java b/src/test/java/org/springframework/data/redis/core/types/ExpirationUnitTests.java index 681ca84b26..d9657bf247 100644 --- a/src/test/java/org/springframework/data/redis/core/types/ExpirationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/types/ExpirationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/core/types/RedisClientInfoUnitTests.java b/src/test/java/org/springframework/data/redis/core/types/RedisClientInfoUnitTests.java index 893f9915ab..cdf85e7138 100644 --- a/src/test/java/org/springframework/data/redis/core/types/RedisClientInfoUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/types/RedisClientInfoUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/domain/geo/BoundingBoxUnitTests.java b/src/test/java/org/springframework/data/redis/domain/geo/BoundingBoxUnitTests.java index ab40d19bc7..5b5e642512 100644 --- a/src/test/java/org/springframework/data/redis/domain/geo/BoundingBoxUnitTests.java +++ b/src/test/java/org/springframework/data/redis/domain/geo/BoundingBoxUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/examples/ReactiveRedisApplication.java b/src/test/java/org/springframework/data/redis/examples/ReactiveRedisApplication.java index 4cac7b2eb1..1689d07a26 100644 --- a/src/test/java/org/springframework/data/redis/examples/ReactiveRedisApplication.java +++ b/src/test/java/org/springframework/data/redis/examples/ReactiveRedisApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/examples/RedisApplication.java b/src/test/java/org/springframework/data/redis/examples/RedisApplication.java index 21b01e228d..4ce9118321 100644 --- a/src/test/java/org/springframework/data/redis/examples/RedisApplication.java +++ b/src/test/java/org/springframework/data/redis/examples/RedisApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListenerIntegrationTests.java b/src/test/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListenerIntegrationTests.java index 1ce50709dc..ddf8fdd66b 100644 --- a/src/test/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListenerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListenerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListenerUnitTests.java b/src/test/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListenerUnitTests.java index 6402df5c4a..bc39e548e3 100644 --- a/src/test/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListenerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/listener/KeyExpirationEventMessageListenerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/PubSubAwaitUtil.java b/src/test/java/org/springframework/data/redis/listener/PubSubAwaitUtil.java index 66ec4a4207..557903967c 100644 --- a/src/test/java/org/springframework/data/redis/listener/PubSubAwaitUtil.java +++ b/src/test/java/org/springframework/data/redis/listener/PubSubAwaitUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/PubSubResubscribeTests.java b/src/test/java/org/springframework/data/redis/listener/PubSubResubscribeTests.java index 540a619313..63e441d77e 100644 --- a/src/test/java/org/springframework/data/redis/listener/PubSubResubscribeTests.java +++ b/src/test/java/org/springframework/data/redis/listener/PubSubResubscribeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/PubSubTestParams.java b/src/test/java/org/springframework/data/redis/listener/PubSubTestParams.java index 78d03c6b1f..b034764d48 100644 --- a/src/test/java/org/springframework/data/redis/listener/PubSubTestParams.java +++ b/src/test/java/org/springframework/data/redis/listener/PubSubTestParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/PubSubTests.java b/src/test/java/org/springframework/data/redis/listener/PubSubTests.java index 38413dc5d9..1e0fb72dbb 100644 --- a/src/test/java/org/springframework/data/redis/listener/PubSubTests.java +++ b/src/test/java/org/springframework/data/redis/listener/PubSubTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/ReactiveOperationsTestParams.java b/src/test/java/org/springframework/data/redis/listener/ReactiveOperationsTestParams.java index e1e71a7560..e31b479145 100644 --- a/src/test/java/org/springframework/data/redis/listener/ReactiveOperationsTestParams.java +++ b/src/test/java/org/springframework/data/redis/listener/ReactiveOperationsTestParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerIntegrationTests.java b/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerIntegrationTests.java index 0e167fc65c..e990cea5f2 100644 --- a/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerUnitTests.java b/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerUnitTests.java index 256c5f65dd..f5e99bff47 100644 --- a/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerFailureIntegrationTests.java b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerFailureIntegrationTests.java index 59b0d9bd52..6ecd8f4b75 100644 --- a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerFailureIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerFailureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerIntegrationTests.java b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerIntegrationTests.java index 4e4f571476..bea301b046 100644 --- a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java index 06a9f567c1..ea7dfeb557 100644 --- a/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/listener/RedisMessageListenerContainerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/SubscriptionConnectionTests.java b/src/test/java/org/springframework/data/redis/listener/SubscriptionConnectionTests.java index 4d0701a3ea..6237e7e48f 100644 --- a/src/test/java/org/springframework/data/redis/listener/SubscriptionConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/listener/SubscriptionConnectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/adapter/ContainerXmlSetupIntegrationTests.java b/src/test/java/org/springframework/data/redis/listener/adapter/ContainerXmlSetupIntegrationTests.java index ffa2f25f3c..48849ad11a 100644 --- a/src/test/java/org/springframework/data/redis/listener/adapter/ContainerXmlSetupIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/listener/adapter/ContainerXmlSetupIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/adapter/MessageListenerUnitTests.java b/src/test/java/org/springframework/data/redis/listener/adapter/MessageListenerUnitTests.java index a8735c93ec..876118bd60 100644 --- a/src/test/java/org/springframework/data/redis/listener/adapter/MessageListenerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/listener/adapter/MessageListenerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/adapter/RedisMDP.java b/src/test/java/org/springframework/data/redis/listener/adapter/RedisMDP.java index 6536e0e79c..63788ffa7a 100644 --- a/src/test/java/org/springframework/data/redis/listener/adapter/RedisMDP.java +++ b/src/test/java/org/springframework/data/redis/listener/adapter/RedisMDP.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/listener/adapter/ThrowableMessageListener.java b/src/test/java/org/springframework/data/redis/listener/adapter/ThrowableMessageListener.java index 0d6465b656..eca3ff4e95 100644 --- a/src/test/java/org/springframework/data/redis/listener/adapter/ThrowableMessageListener.java +++ b/src/test/java/org/springframework/data/redis/listener/adapter/ThrowableMessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/mapping/AbstractHashMapperTests.java b/src/test/java/org/springframework/data/redis/mapping/AbstractHashMapperTests.java index af3cefe216..3807d13680 100644 --- a/src/test/java/org/springframework/data/redis/mapping/AbstractHashMapperTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/AbstractHashMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/mapping/BeanUtilsHashMapperTests.java b/src/test/java/org/springframework/data/redis/mapping/BeanUtilsHashMapperTests.java index 94d7488762..301056e3ea 100644 --- a/src/test/java/org/springframework/data/redis/mapping/BeanUtilsHashMapperTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/BeanUtilsHashMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperFlatteningUnitTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperFlatteningUnitTests.java index 593d7ec594..5e5e2c95cf 100644 --- a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperFlatteningUnitTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperFlatteningUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperIntegrationTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperIntegrationTests.java index c116dce5c5..8b96474031 100644 --- a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperNonFlatteningUnitTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperNonFlatteningUnitTests.java index 18c2f2b69b..688058c0ce 100644 --- a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperNonFlatteningUnitTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperNonFlatteningUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java index 9b0735e6bd..5b8f392619 100644 --- a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/mapping/ObjectHashMapperTests.java b/src/test/java/org/springframework/data/redis/mapping/ObjectHashMapperTests.java index 31b74f2d57..823338e187 100644 --- a/src/test/java/org/springframework/data/redis/mapping/ObjectHashMapperTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/ObjectHashMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/RedisRepositoryClusterIntegrationTests.java b/src/test/java/org/springframework/data/redis/repository/RedisRepositoryClusterIntegrationTests.java index b02fea7953..b8623bb2e9 100644 --- a/src/test/java/org/springframework/data/redis/repository/RedisRepositoryClusterIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/repository/RedisRepositoryClusterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTestBase.java b/src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTestBase.java index 94009df445..780f630b6b 100644 --- a/src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTestBase.java +++ b/src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTests.java index 120fdc4c96..8437f2ef73 100644 --- a/src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/CdiExtensionIntegrationTests.java b/src/test/java/org/springframework/data/redis/repository/cdi/CdiExtensionIntegrationTests.java index dfbbffd692..a0b221b5aa 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/CdiExtensionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/CdiExtensionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/Person.java b/src/test/java/org/springframework/data/redis/repository/cdi/Person.java index 00d23ff94b..5a1d07e737 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/Person.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/Person.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/PersonDB.java b/src/test/java/org/springframework/data/redis/repository/cdi/PersonDB.java index 237f08a970..38cc84d46b 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/PersonDB.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/PersonDB.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/PersonFragment.java b/src/test/java/org/springframework/data/redis/repository/cdi/PersonFragment.java index a5d81e8c00..08fee652ab 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/PersonFragment.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/PersonFragment.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/PersonFragmentImpl.java b/src/test/java/org/springframework/data/redis/repository/cdi/PersonFragmentImpl.java index 8bfc9c50ab..b17bdc9f3d 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/PersonFragmentImpl.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/PersonFragmentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/PersonRepository.java b/src/test/java/org/springframework/data/redis/repository/cdi/PersonRepository.java index ac82b1b4fe..4e6a6b6236 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/PersonRepository.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/PersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/QualifiedPersonRepository.java b/src/test/java/org/springframework/data/redis/repository/cdi/QualifiedPersonRepository.java index c04bf3d6bb..5dec512831 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/QualifiedPersonRepository.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/QualifiedPersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/RedisCdiDependenciesProducer.java b/src/test/java/org/springframework/data/redis/repository/cdi/RedisCdiDependenciesProducer.java index 5f9f8b32ec..67d0053687 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/RedisCdiDependenciesProducer.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/RedisCdiDependenciesProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/cdi/RepositoryConsumer.java b/src/test/java/org/springframework/data/redis/repository/cdi/RepositoryConsumer.java index 300f37f818..0f9701bc94 100644 --- a/src/test/java/org/springframework/data/redis/repository/cdi/RepositoryConsumer.java +++ b/src/test/java/org/springframework/data/redis/repository/cdi/RepositoryConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrarUnitTests.java b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrarUnitTests.java index 7e3d27fbf3..be2356227a 100644 --- a/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrarUnitTests.java +++ b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoriesRegistrarUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtensionUnitTests.java b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtensionUnitTests.java index 008cb091e3..b515de2ac4 100644 --- a/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtensionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtensionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationUnitTests.java index b2a21f454f..9ef5506258 100644 --- a/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformationUnitTests.java b/src/test/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformationUnitTests.java index 746a7e4cca..6637d6f83e 100644 --- a/src/test/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/repository/core/MappingRedisEntityInformationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/query/ExampleQueryMapperUnitTests.java b/src/test/java/org/springframework/data/redis/repository/query/ExampleQueryMapperUnitTests.java index 445beb8f38..9a99d12538 100644 --- a/src/test/java/org/springframework/data/redis/repository/query/ExampleQueryMapperUnitTests.java +++ b/src/test/java/org/springframework/data/redis/repository/query/ExampleQueryMapperUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/query/RedisQueryCreatorUnitTests.java b/src/test/java/org/springframework/data/redis/repository/query/RedisQueryCreatorUnitTests.java index 8cd54ae12c..c088ff751c 100644 --- a/src/test/java/org/springframework/data/redis/repository/query/RedisQueryCreatorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/repository/query/RedisQueryCreatorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutorIntegrationTests.java b/src/test/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutorIntegrationTests.java index ebcec9b870..0c29bc6e6a 100644 --- a/src/test/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutorIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutorIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/serializer/DefaultRedisElementReaderUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/DefaultRedisElementReaderUnitTests.java index 7be9d472cf..4e9c9759ef 100644 --- a/src/test/java/org/springframework/data/redis/serializer/DefaultRedisElementReaderUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/DefaultRedisElementReaderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/serializer/DefaultRedisElementWriterUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/DefaultRedisElementWriterUnitTests.java index 440c45ac04..e971c6dd8b 100644 --- a/src/test/java/org/springframework/data/redis/serializer/DefaultRedisElementWriterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/DefaultRedisElementWriterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java index c91e6b46f7..70f7d46615 100644 --- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializerTests.java b/src/test/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializerTests.java index dab08d2c0c..da98eff85d 100644 --- a/src/test/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializerTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/serializer/RedisSerializationContextUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/RedisSerializationContextUnitTests.java index e435f57b6e..0d15d0b23b 100644 --- a/src/test/java/org/springframework/data/redis/serializer/RedisSerializationContextUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/RedisSerializationContextUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/serializer/SerializableDomainClass.java b/src/test/java/org/springframework/data/redis/serializer/SerializableDomainClass.java index 46e5712ba1..f02625ce68 100644 --- a/src/test/java/org/springframework/data/redis/serializer/SerializableDomainClass.java +++ b/src/test/java/org/springframework/data/redis/serializer/SerializableDomainClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2016-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/serializer/SimpleRedisSerializerTests.java b/src/test/java/org/springframework/data/redis/serializer/SimpleRedisSerializerTests.java index af21b6a1fb..f76a4fa5d6 100644 --- a/src/test/java/org/springframework/data/redis/serializer/SimpleRedisSerializerTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/SimpleRedisSerializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/serializer/StringRedisSerializerUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/StringRedisSerializerUnitTests.java index 88c99848a8..58d6fd3f93 100644 --- a/src/test/java/org/springframework/data/redis/serializer/StringRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/StringRedisSerializerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java b/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java index 4e5d620ccf..26318b1448 100644 --- a/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/stream/AbstractStreamMessageListenerContainerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/stream/JedisStreamMessageListenerContainerIntegrationTests.java b/src/test/java/org/springframework/data/redis/stream/JedisStreamMessageListenerContainerIntegrationTests.java index e8c2ed56ec..e6e27f7548 100644 --- a/src/test/java/org/springframework/data/redis/stream/JedisStreamMessageListenerContainerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/stream/JedisStreamMessageListenerContainerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/stream/LettuceStreamMessageListenerContainerIntegrationTests.java b/src/test/java/org/springframework/data/redis/stream/LettuceStreamMessageListenerContainerIntegrationTests.java index 5a6357fefe..91c6f42c19 100644 --- a/src/test/java/org/springframework/data/redis/stream/LettuceStreamMessageListenerContainerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/stream/LettuceStreamMessageListenerContainerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/stream/ReadOffsetStrategyUnitTests.java b/src/test/java/org/springframework/data/redis/stream/ReadOffsetStrategyUnitTests.java index dac2fe205f..dfb1e24c7f 100644 --- a/src/test/java/org/springframework/data/redis/stream/ReadOffsetStrategyUnitTests.java +++ b/src/test/java/org/springframework/data/redis/stream/ReadOffsetStrategyUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/stream/StreamReceiverIntegrationTests.java b/src/test/java/org/springframework/data/redis/stream/StreamReceiverIntegrationTests.java index 5abe16c2c3..ea5369856c 100644 --- a/src/test/java/org/springframework/data/redis/stream/StreamReceiverIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/stream/StreamReceiverIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java index 826247c56f..ad510f97e5 100644 --- a/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/BoundKeyOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/BoundKeyParams.java b/src/test/java/org/springframework/data/redis/support/BoundKeyParams.java index 3624336f2c..89b7270a3d 100644 --- a/src/test/java/org/springframework/data/redis/support/BoundKeyParams.java +++ b/src/test/java/org/springframework/data/redis/support/BoundKeyParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/atomic/AtomicCountersParam.java b/src/test/java/org/springframework/data/redis/support/atomic/AtomicCountersParam.java index f958c93fce..15393f3cc6 100644 --- a/src/test/java/org/springframework/data/redis/support/atomic/AtomicCountersParam.java +++ b/src/test/java/org/springframework/data/redis/support/atomic/AtomicCountersParam.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/atomic/CompareAndSetIntegrationIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/atomic/CompareAndSetIntegrationIntegrationTests.java index 0d4b899cf2..6e436a7365 100644 --- a/src/test/java/org/springframework/data/redis/support/atomic/CompareAndSetIntegrationIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/atomic/CompareAndSetIntegrationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicDoubleIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicDoubleIntegrationTests.java index 6a1302c0c9..f8941ffe8a 100644 --- a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicDoubleIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicDoubleIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicDoubleUnitTests.java b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicDoubleUnitTests.java index c504e5fe61..22ed35e76b 100644 --- a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicDoubleUnitTests.java +++ b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicDoubleUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicIntegerIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicIntegerIntegrationTests.java index cd7a4332bf..d5d9ea8bcc 100644 --- a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicIntegerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicIntegerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicIntegerUnitTests.java b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicIntegerUnitTests.java index eeb14ccc2f..3e58c78919 100644 --- a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicIntegerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicIntegerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicLongIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicLongIntegrationTests.java index 21e22125a6..21ba2729f4 100644 --- a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicLongIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicLongIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicLongUnitTests.java b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicLongUnitTests.java index cd4a349183..1e22396d63 100644 --- a/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicLongUnitTests.java +++ b/src/test/java/org/springframework/data/redis/support/atomic/RedisAtomicLongUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisCollectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisCollectionIntegrationTests.java index 5f6b5ca85b..e4a0cbcd8e 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisCollectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisCollectionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisCollectionUnitTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisCollectionUnitTests.java index 47f75e4576..6fe195d0fc 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisCollectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisCollectionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisListIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisListIntegrationTests.java index 1306f0d795..692a967c1f 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisListIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisListIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java index 3f4e17d439..435d5b4500 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisSetIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisSetIntegrationTests.java index 60e839548e..8319ee373a 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisSetIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisSetIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java index 20e647c45f..9f443dc7ff 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/CollectionTestParams.java b/src/test/java/org/springframework/data/redis/support/collections/CollectionTestParams.java index 3ea95932a5..8b9063497f 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/CollectionTestParams.java +++ b/src/test/java/org/springframework/data/redis/support/collections/CollectionTestParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/DefaultRedisMapUnitUnitTests.java b/src/test/java/org/springframework/data/redis/support/collections/DefaultRedisMapUnitUnitTests.java index f946511750..637a4a690d 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/DefaultRedisMapUnitUnitTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/DefaultRedisMapUnitUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/RedisCollectionFactoryBeanTests.java b/src/test/java/org/springframework/data/redis/support/collections/RedisCollectionFactoryBeanTests.java index 4075821b4e..ecff984147 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/RedisCollectionFactoryBeanTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/RedisCollectionFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/RedisListIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/RedisListIntegrationTests.java index 9102a0f1a8..bceca49729 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/RedisListIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/RedisListIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/RedisMapIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/RedisMapIntegrationTests.java index d6ac4b7911..b54c1ba7ec 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/RedisMapIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/RedisMapIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/RedisPropertiesIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/RedisPropertiesIntegrationTests.java index 9f4fd59b7f..17e63c3113 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/RedisPropertiesIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/RedisPropertiesIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/RedisSetIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/RedisSetIntegrationTests.java index cde7e8d8dc..6a0228d346 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/RedisSetIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/RedisSetIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/RedisZSetIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/RedisZSetIntegrationTests.java index 86b3fa4a50..d508e0af78 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/RedisZSetIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/RedisZSetIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/support/collections/SupportXmlIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/SupportXmlIntegrationTests.java index 656b8966d9..d9857db8e5 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/SupportXmlIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/SupportXmlIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2024 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/XstreamOxmSerializerSingleton.java b/src/test/java/org/springframework/data/redis/test/XstreamOxmSerializerSingleton.java index ee03b5711c..c22b4216af 100644 --- a/src/test/java/org/springframework/data/redis/test/XstreamOxmSerializerSingleton.java +++ b/src/test/java/org/springframework/data/redis/test/XstreamOxmSerializerSingleton.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledIfLongRunningTest.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledIfLongRunningTest.java index 436be6983a..cee6fede5e 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledIfLongRunningTest.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledIfLongRunningTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnCommand.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnCommand.java index 346c359987..a9db6d17f5 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnCommand.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnCommandCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnCommandCondition.java index 9d5887ec63..1844acbb8a 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnCommandCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnCommandCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailable.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailable.java index a5f3c31411..0d396cf41f 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailable.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailable.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailableCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailableCondition.java index 88ac8dcbc5..b7ec1f82d9 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailableCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisAvailableCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterAvailable.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterAvailable.java index feebd4ad6d..f25cd153e0 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterAvailable.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterAvailable.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterCondition.java index dba3c6071b..fe30380184 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisClusterCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriver.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriver.java index c904ae9543..7e2da2bef0 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriver.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriver.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriverCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriverCondition.java index b65e66d040..68286b5e86 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriverCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisDriverCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelAvailable.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelAvailable.java index b2f225026f..42d29b9a11 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelAvailable.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelAvailable.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelCondition.java index 2d7fe35671..26b2665d4d 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisSentinelCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersion.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersion.java index e3c6ab3d67..326e27be64 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersion.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersion.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersionCondition.java b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersionCondition.java index 9000158666..5aec897431 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersionCondition.java +++ b/src/test/java/org/springframework/data/redis/test/condition/EnabledOnRedisVersionCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/LongRunningTest.java b/src/test/java/org/springframework/data/redis/test/condition/LongRunningTest.java index ce1495bcf0..2114fa1bbc 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/LongRunningTest.java +++ b/src/test/java/org/springframework/data/redis/test/condition/LongRunningTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/RedisConditions.java b/src/test/java/org/springframework/data/redis/test/condition/RedisConditions.java index 003ade86cd..77ce6bf311 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/RedisConditions.java +++ b/src/test/java/org/springframework/data/redis/test/condition/RedisConditions.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/RedisDetector.java b/src/test/java/org/springframework/data/redis/test/condition/RedisDetector.java index 5195c3f28a..d0f3e50fcf 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/RedisDetector.java +++ b/src/test/java/org/springframework/data/redis/test/condition/RedisDetector.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/condition/RedisDriver.java b/src/test/java/org/springframework/data/redis/test/condition/RedisDriver.java index 6c0240275f..6c73e50fb0 100644 --- a/src/test/java/org/springframework/data/redis/test/condition/RedisDriver.java +++ b/src/test/java/org/springframework/data/redis/test/condition/RedisDriver.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/JedisExtension.java b/src/test/java/org/springframework/data/redis/test/extension/JedisExtension.java index cd77b39ee4..cdf9225e8b 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/JedisExtension.java +++ b/src/test/java/org/springframework/data/redis/test/extension/JedisExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/LettuceExtension.java b/src/test/java/org/springframework/data/redis/test/extension/LettuceExtension.java index a87cb5f1dc..949a0fba7c 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/LettuceExtension.java +++ b/src/test/java/org/springframework/data/redis/test/extension/LettuceExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/LettuceTestClientResources.java b/src/test/java/org/springframework/data/redis/test/extension/LettuceTestClientResources.java index 8cdc9be407..90a57e8a3a 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/LettuceTestClientResources.java +++ b/src/test/java/org/springframework/data/redis/test/extension/LettuceTestClientResources.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/RedisCluster.java b/src/test/java/org/springframework/data/redis/test/extension/RedisCluster.java index 10c2c236a9..5af80603ce 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/RedisCluster.java +++ b/src/test/java/org/springframework/data/redis/test/extension/RedisCluster.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/RedisSentinel.java b/src/test/java/org/springframework/data/redis/test/extension/RedisSentinel.java index c589838987..85218ddb5b 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/RedisSentinel.java +++ b/src/test/java/org/springframework/data/redis/test/extension/RedisSentinel.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/RedisStanalone.java b/src/test/java/org/springframework/data/redis/test/extension/RedisStanalone.java index 625b518465..e04679eb94 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/RedisStanalone.java +++ b/src/test/java/org/springframework/data/redis/test/extension/RedisStanalone.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/ShutdownQueue.java b/src/test/java/org/springframework/data/redis/test/extension/ShutdownQueue.java index 8f8d760351..f818e17e55 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/ShutdownQueue.java +++ b/src/test/java/org/springframework/data/redis/test/extension/ShutdownQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/MethodArgumentsProvider.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/MethodArgumentsProvider.java index ed3b602908..896192fc83 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/MethodArgumentsProvider.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/MethodArgumentsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/MethodSource.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/MethodSource.java index 4c9bc494b9..38e4477242 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/MethodSource.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/MethodSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTest.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTest.java index 9f80af0bc8..efdef385d3 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTest.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTestExtension.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTestExtension.java index 3cb80348f2..0e295527ed 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTestExtension.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedRedisTestExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestContext.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestContext.java index af4e61eb18..6d1dd069f1 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestContext.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestInvocationContext.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestInvocationContext.java index 5e3b365405..658cd29b20 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestInvocationContext.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestInvocationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestNameFormatter.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestNameFormatter.java index b88598baa4..7c02a0171f 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestNameFormatter.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestNameFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestParameterResolver.java b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestParameterResolver.java index 4a644c744b..adcff56f32 100644 --- a/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestParameterResolver.java +++ b/src/test/java/org/springframework/data/redis/test/extension/parametrized/ParameterizedTestParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/util/CollectionAwareComparator.java b/src/test/java/org/springframework/data/redis/test/util/CollectionAwareComparator.java index c20657d209..c505f89375 100644 --- a/src/test/java/org/springframework/data/redis/test/util/CollectionAwareComparator.java +++ b/src/test/java/org/springframework/data/redis/test/util/CollectionAwareComparator.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/util/HexStringUtils.java b/src/test/java/org/springframework/data/redis/test/util/HexStringUtils.java index f6401d6ee5..388d877fdf 100644 --- a/src/test/java/org/springframework/data/redis/test/util/HexStringUtils.java +++ b/src/test/java/org/springframework/data/redis/test/util/HexStringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/util/MockitoUtils.java b/src/test/java/org/springframework/data/redis/test/util/MockitoUtils.java index d9928f6dfa..24d5eb41b3 100644 --- a/src/test/java/org/springframework/data/redis/test/util/MockitoUtils.java +++ b/src/test/java/org/springframework/data/redis/test/util/MockitoUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/test/util/RedisTestData.java b/src/test/java/org/springframework/data/redis/test/util/RedisTestData.java index e1710741f5..959f0da9bb 100644 --- a/src/test/java/org/springframework/data/redis/test/util/RedisTestData.java +++ b/src/test/java/org/springframework/data/redis/test/util/RedisTestData.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/util/ByteUtilsUnitTests.java b/src/test/java/org/springframework/data/redis/util/ByteUtilsUnitTests.java index 068c0ea716..6a60dac053 100644 --- a/src/test/java/org/springframework/data/redis/util/ByteUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/redis/util/ByteUtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 the original author or authors. + * Copyright 2021-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/data/redis/util/ConnectionVerifier.java b/src/test/java/org/springframework/data/redis/util/ConnectionVerifier.java index 32dc8dc89b..90af8cb259 100644 --- a/src/test/java/org/springframework/data/redis/util/ConnectionVerifier.java +++ b/src/test/java/org/springframework/data/redis/util/ConnectionVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/PartialUpdateExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/PartialUpdateExtensionsUnitTests.kt index eb89fb123f..846cf66c04 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/PartialUpdateExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/PartialUpdateExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveGeoOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveGeoOperationsExtensionsUnitTests.kt index ce18d3cfee..ccb6737846 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveGeoOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveGeoOperationsExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveHashOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveHashOperationsExtensionsUnitTests.kt index 11a6c62164..61b00cde04 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveHashOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveHashOperationsExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveHyperLogLogOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveHyperLogLogOperationsExtensionsUnitTests.kt index df1535e475..b1dcfe15df 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveHyperLogLogOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveHyperLogLogOperationsExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveListOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveListOperationsExtensionsUnitTests.kt index e9a2519208..779b44371c 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveListOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveListOperationsExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensionsUnitTests.kt index 970d81faca..62a6c968f3 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveSetOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveSetOperationsExtensionsUnitTests.kt index 30af755677..f7ec83678d 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveSetOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveSetOperationsExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveStreamOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveStreamOperationsExtensionsUnitTests.kt index 9e14277fb4..22ea0603af 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveStreamOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveStreamOperationsExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveValueOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveValueOperationsExtensionsUnitTests.kt index 748ff04a05..50ad68e9d2 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveValueOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveValueOperationsExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveZSetOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveZSetOperationsExtensionsUnitTests.kt index 9f7f52175f..91c75933ba 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveZSetOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveZSetOperationsExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/kotlin/org/springframework/data/redis/core/script/RedisScriptExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/script/RedisScriptExtensionsUnitTests.kt index 219c1ace31..dc40354c98 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/script/RedisScriptExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/script/RedisScriptExtensionsUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 the original author or authors. + * Copyright 2022-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From e3c91e94f156b1047634160972b1d592be68f489 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 10 Jan 2025 11:06:33 +0100 Subject: [PATCH 118/187] Switch from CLA to DCO. See spring-projects/spring-data-build#2471 --- .github/dco.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/dco.yml diff --git a/.github/dco.yml b/.github/dco.yml new file mode 100644 index 0000000000..0c4b142e9a --- /dev/null +++ b/.github/dco.yml @@ -0,0 +1,2 @@ +require: + members: false From b7381ac6bb269b929fcbcddb10ea165ac2ce6da7 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Thu, 12 Dec 2024 22:32:09 +0100 Subject: [PATCH 119/187] Prepare connection pool. We now prepare the pool when the connection factory is started respective the pool has been instantiated. Closes #3072 --- .../LettucePoolingConnectionProvider.java | 15 ++++++++++++-- ...ucePoolingConnectionProviderUnitTests.java | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java index 88ef7561ab..57d3ad0842 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java @@ -55,6 +55,7 @@ * * @author Mark Paluch * @author Christoph Strobl + * @author Asmir Mustafic * @since 2.0 * @see #getConnection(Class) */ @@ -90,8 +91,18 @@ class LettucePoolingConnectionProvider implements LettuceConnectionProvider, Red public > T getConnection(Class connectionType) { GenericObjectPool> pool = pools.computeIfAbsent(connectionType, poolType -> { - return ConnectionPoolSupport.createGenericObjectPool(() -> connectionProvider.getConnection(connectionType), - poolConfig, false); + + GenericObjectPool> newPool = ConnectionPoolSupport + .createGenericObjectPool(() -> connectionProvider.getConnection(connectionType), poolConfig, false); + + try { + newPool.preparePool(); + + } catch (Exception ex) { + throw new PoolException("Could not prepare the pool", ex); + } + + return newPool; }); try { diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java index bc18a584cc..1722319ffc 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java @@ -20,6 +20,7 @@ import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.async.RedisAsyncCommands; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -27,11 +28,13 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder; /** * Unit tests for {@link LettucePoolingConnectionProvider}. * * @author Mark Paluch + * @author Asmir Mustafic */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -70,4 +73,21 @@ void shouldDiscardTransactionOnReleaseOnActiveTransaction() { verify(commandsMock).discard(); } + + @Test + void shouldPrepareThePool() { + + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + poolConfig.setMinIdle(5); + poolConfig.setMaxIdle(8); + poolConfig.setMaxTotal(10); + + LettucePoolingClientConfiguration config = new LettucePoolingClientConfigurationBuilder().poolConfig(poolConfig) + .build(); + + LettucePoolingConnectionProvider provider = new LettucePoolingConnectionProvider(connectionProviderMock, config); + + provider.getConnection(StatefulRedisConnection.class); + verify(connectionProviderMock, times(5)).getConnection(any()); + } } From 2bcdd36a368d68e5ecffc204294c890556017079 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 10 Jan 2025 14:01:05 +0100 Subject: [PATCH 120/187] Polishing. Prepare pool for Jedis as well. Introduce generics for Add ticket references to tests. Fix generics for GenericObjectPoolConfig using Lettuce. See #3072 --- .../jedis/JedisConnectionFactory.java | 6 ++++ ...aultLettucePoolingClientConfiguration.java | 7 +++-- .../LettucePoolingClientConfiguration.java | 8 +++-- .../LettucePoolingConnectionProvider.java | 3 +- .../JedisConnectionFactoryUnitTests.java | 30 +++++++++++++++++-- ...ucePoolingConnectionProviderUnitTests.java | 5 ++-- 6 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java index 419b82550e..01cb4badec 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java @@ -730,6 +730,12 @@ public void start() { if (getUsePool() && !isRedisClusterAware()) { this.pool = createPool(); + + try { + this.pool.preparePool(); + } catch (Exception ex) { + throw new PoolException("Could not prepare the pool", ex); + } } if (isRedisClusterAware()) { diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java index 40d02becd4..7d8cfa4d78 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/DefaultLettucePoolingClientConfiguration.java @@ -18,6 +18,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.ReadFrom; import io.lettuce.core.SslVerifyMode; +import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.resource.ClientResources; import java.time.Duration; @@ -37,10 +38,10 @@ class DefaultLettucePoolingClientConfiguration implements LettucePoolingClientConfiguration { private final LettuceClientConfiguration clientConfiguration; - private final GenericObjectPoolConfig poolConfig; + private final GenericObjectPoolConfig> poolConfig; DefaultLettucePoolingClientConfiguration(LettuceClientConfiguration clientConfiguration, - GenericObjectPoolConfig poolConfig) { + GenericObjectPoolConfig> poolConfig) { this.clientConfiguration = clientConfiguration; this.poolConfig = poolConfig; @@ -108,7 +109,7 @@ public Duration getShutdownQuietPeriod() { } @Override - public GenericObjectPoolConfig getPoolConfig() { + public GenericObjectPoolConfig> getPoolConfig() { return poolConfig; } } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfiguration.java index bb72d32ccc..dd06a6d3b7 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingClientConfiguration.java @@ -17,6 +17,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.ReadFrom; +import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.resource.ClientResources; import java.time.Duration; @@ -39,7 +40,7 @@ public interface LettucePoolingClientConfiguration extends LettuceClientConfigur /** * @return the {@link GenericObjectPoolConfig}. Never {@literal null}. */ - GenericObjectPoolConfig getPoolConfig(); + GenericObjectPoolConfig> getPoolConfig(); /** * Creates a new {@link LettucePoolingClientConfigurationBuilder} to build {@link LettucePoolingClientConfiguration} @@ -91,7 +92,7 @@ static LettucePoolingClientConfiguration defaultConfiguration() { */ class LettucePoolingClientConfigurationBuilder extends LettuceClientConfigurationBuilder { - GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + GenericObjectPoolConfig> poolConfig = new GenericObjectPoolConfig<>(); LettucePoolingClientConfigurationBuilder() { super(); @@ -163,7 +164,8 @@ public LettucePoolingClientConfigurationBuilder clientName(String clientName) { * * @param poolConfig must not be {@literal null}. */ - public LettucePoolingClientConfigurationBuilder poolConfig(GenericObjectPoolConfig poolConfig) { + public LettucePoolingClientConfigurationBuilder poolConfig( + GenericObjectPoolConfig> poolConfig) { Assert.notNull(poolConfig, "PoolConfig must not be null"); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java index 57d3ad0842..086c9ce763 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java @@ -64,7 +64,7 @@ class LettucePoolingConnectionProvider implements LettuceConnectionProvider, Red private static final Log log = LogFactory.getLog(LettucePoolingConnectionProvider.class); private final LettuceConnectionProvider connectionProvider; - private final GenericObjectPoolConfig poolConfig; + private final GenericObjectPoolConfig> poolConfig; private final Map, GenericObjectPool>> poolRef = new ConcurrentHashMap<>( 32); @@ -97,7 +97,6 @@ class LettucePoolingConnectionProvider implements LettuceConnectionProvider, Red try { newPool.preparePool(); - } catch (Exception ex) { throw new PoolException("Could not prepare the pool", ex); } diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java index 9b1496c719..d766502a8a 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java @@ -19,10 +19,12 @@ import static org.mockito.Mockito.*; import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.RedisProtocol; +import redis.clients.jedis.util.Pool; import java.io.IOException; import java.security.NoSuchAlgorithmException; @@ -265,6 +267,28 @@ void shouldApplyClientConfiguration() throws NoSuchAlgorithmException { assertThat(connectionFactory.getPoolConfig()).isSameAs(poolConfig); } + @Test // GH-3072 + void shouldInitializePool() throws Exception { + + JedisPoolConfig poolConfig = new JedisPoolConfig(); + Pool poolMock = mock(Pool.class); + + JedisClientConfiguration configuration = JedisClientConfiguration.builder() // + .usePooling().poolConfig(poolConfig) // + .build(); + + connectionFactory = new JedisConnectionFactory(new RedisStandaloneConfiguration(), configuration) { + @Override + protected Pool createRedisPool() { + return poolMock; + } + }; + + connectionFactory.afterPropertiesSet(); + + verify(poolMock).preparePool(); + } + @Test // DATAREDIS-574 void shouldReturnStandaloneConfiguration() { @@ -382,12 +406,12 @@ void earlyStartupDoesNotStartConnectionFactory() { private JedisConnectionFactory initSpyedConnectionFactory(RedisSentinelConfiguration sentinelConfiguration, @Nullable JedisPoolConfig poolConfig) { + Pool poolMock = mock(Pool.class); // we have to use a spy here as jedis would start connecting to redis sentinels when the pool is created. JedisConnectionFactory connectionFactorySpy = spy(new JedisConnectionFactory(sentinelConfiguration, poolConfig)); - doReturn(null).when(connectionFactorySpy).createRedisSentinelPool(any(RedisSentinelConfiguration.class)); - - doReturn(null).when(connectionFactorySpy).createRedisPool(); + doReturn(poolMock).when(connectionFactorySpy).createRedisSentinelPool(any(RedisSentinelConfiguration.class)); + doReturn(poolMock).when(connectionFactorySpy).createRedisPool(); return connectionFactorySpy; } diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java index 1722319ffc..4c4cfe735a 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProviderUnitTests.java @@ -17,6 +17,7 @@ import static org.mockito.Mockito.*; +import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.async.RedisAsyncCommands; @@ -74,10 +75,10 @@ void shouldDiscardTransactionOnReleaseOnActiveTransaction() { verify(commandsMock).discard(); } - @Test + @Test // GH-3072 void shouldPrepareThePool() { - GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + GenericObjectPoolConfig> poolConfig = new GenericObjectPoolConfig<>(); poolConfig.setMinIdle(5); poolConfig.setMaxIdle(8); poolConfig.setMaxTotal(10); From 0c90dea0122c1640707c03f8794f9e9d5f2c756a Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 13 Jan 2025 14:35:13 +0100 Subject: [PATCH 121/187] Use Integer return type for Number return types except Double and Float. Lettuce's ValueOutput doesn't RESP 3 long values so we must use IntegerOutput instead. Closes #3090 --- .../data/redis/connection/ReturnType.java | 6 +++++- .../data/redis/connection/ReturnTypeUnitTests.java | 13 ++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ReturnType.java b/src/main/java/org/springframework/data/redis/connection/ReturnType.java index 2ba37e2ab9..5085e6e6bd 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReturnType.java +++ b/src/main/java/org/springframework/data/redis/connection/ReturnType.java @@ -73,7 +73,11 @@ public static ReturnType fromJavaType(@Nullable Class javaType) { return ReturnType.BOOLEAN; } - if (ClassUtils.isAssignable(Long.class, javaType)) { + if (ClassUtils.isAssignable(Double.class, javaType) || ClassUtils.isAssignable(Float.class, javaType)) { + return ReturnType.VALUE; + } + + if (ClassUtils.isAssignable(Number.class, javaType)) { return ReturnType.INTEGER; } diff --git a/src/test/java/org/springframework/data/redis/connection/ReturnTypeUnitTests.java b/src/test/java/org/springframework/data/redis/connection/ReturnTypeUnitTests.java index a455115c34..fb5c24bf16 100644 --- a/src/test/java/org/springframework/data/redis/connection/ReturnTypeUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ReturnTypeUnitTests.java @@ -34,8 +34,19 @@ class ReturnTypeUnitTests { @ParameterizedTest // DATAREDIS-1245 @ValueSource(classes = { List.class, ArrayList.class, LinkedList.class }) void shouldConsiderListsAsMultiType(Class listClass) { - assertThat(ReturnType.fromJavaType(listClass)).isEqualTo(ReturnType.MULTI); } + @ParameterizedTest // GH-3090 + @ValueSource(classes = { Integer.class, Long.class, Number.class }) + void shouldConsiderIntegerType(Class listClass) { + assertThat(ReturnType.fromJavaType(listClass)).isEqualTo(ReturnType.INTEGER); + } + + @ParameterizedTest // GH-3090 + @ValueSource(classes = { Double.class, Float.class, String.class }) + void shouldConsiderValueType(Class listClass) { + assertThat(ReturnType.fromJavaType(listClass)).isEqualTo(ReturnType.VALUE); + } + } From 36a665234cf409e8b928a5d2812c0b9c39383dbb Mon Sep 17 00:00:00 2001 From: powercheng <43227582+hczs@users.noreply.github.com> Date: Thu, 2 Jan 2025 22:17:49 +0800 Subject: [PATCH 122/187] Fix typo in reference docs. Closes #3082 --- src/main/antora/modules/ROOT/pages/redis/template.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/antora/modules/ROOT/pages/redis/template.adoc b/src/main/antora/modules/ROOT/pages/redis/template.adoc index a43028834d..05af2e789b 100644 --- a/src/main/antora/modules/ROOT/pages/redis/template.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/template.adoc @@ -159,7 +159,7 @@ class MyConfig { } @Bean - ReactiveRedisTemplate ReactiveRedisTemplate(ReactoveRedisConnectionFactory connectionFactory) { + ReactiveRedisTemplate ReactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) { return new ReactiveRedisTemplate<>(connectionFactory, RedisSerializationContext.string()); } } From 0d27943539229cbd9466509a68c09ed39c56795f Mon Sep 17 00:00:00 2001 From: KIMSIWOO Date: Tue, 3 Dec 2024 08:15:35 +0900 Subject: [PATCH 123/187] Fix typo in reference cache example code. Fixed minor typo on example code `build()` to `builder()` Closes #3064 --- src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc b/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc index 014aadb2e7..4e9f48df54 100644 --- a/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/redis-cache.adoc @@ -48,7 +48,7 @@ It is possible to opt in to the locking behavior as follows: [source,java] ---- RedisCacheManager cacheManager = RedisCacheManager - .build(RedisCacheWriter.lockingRedisCacheWriter(connectionFactory)) + .builder(RedisCacheWriter.lockingRedisCacheWriter(connectionFactory)) .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()) ... ---- @@ -77,7 +77,7 @@ The `SCAN` strategy requires a batch size to avoid excessive Redis command round [source,java] ---- RedisCacheManager cacheManager = RedisCacheManager - .build(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.scan(1000))) + .builder(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.scan(1000))) .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()) ... ---- From 6f399ab6de812046dba7a2590be5d31f9819a115 Mon Sep 17 00:00:00 2001 From: kimjinmyeong Date: Tue, 26 Nov 2024 22:49:20 +0900 Subject: [PATCH 124/187] Add missing `@Override` annotation to `GenericJackson2JsonRedisSerializer.useForType`. This fixes an issue where the `useForType` method in GenericJackson2JsonRedisSerializer did not have the `@Override` annotation, making it less clear that it overrides a method from DefaultTypeResolverBuilder. Closes #3053 Original pull request: #3058 --- .../redis/serializer/GenericJackson2JsonRedisSerializer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index 35f7f38865..1d947c9508 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -636,6 +636,7 @@ public ObjectMapper.DefaultTypeResolverBuilder withDefaultImpl(Class defaultI * Boolean, Integer, Double) will never use typing; that is both due to them being concrete and final, and since * actual serializers and deserializers will also ignore any attempts to enforce typing. */ + @Override public boolean useForType(JavaType javaType) { if (javaType.isJavaLangObject()) { From ce2c7caee7e918ae45f7e4b7ee7eae65b54003b5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 15 Jan 2025 14:10:09 +0100 Subject: [PATCH 125/187] Polishing. Add missing Nullable annotations to serializers. See #3053 Original pull request: #3058 --- .../redis/serializer/GenericJackson2JsonRedisSerializer.java | 5 +---- .../data/redis/serializer/GenericToStringSerializer.java | 1 + .../data/redis/serializer/Jackson2JsonRedisSerializer.java | 1 + .../redis/serializer/JdkSerializationRedisSerializer.java | 2 ++ .../springframework/data/redis/serializer/OxmSerializer.java | 1 + .../data/redis/serializer/StringRedisSerializer.java | 2 ++ 6 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index 1d947c9508..a2f2fe258d 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -277,6 +277,7 @@ public byte[] serialize(@Nullable Object value) throws SerializationException { } @Override + @Nullable public Object deserialize(@Nullable byte[] source) throws SerializationException { return deserialize(source, Object.class); } @@ -375,10 +376,6 @@ protected JavaType resolveType(byte[] source, Class type) throws IOException /** * Lenient variant of ObjectMapper._readTreeAndClose using a strict {@link JsonNodeDeserializer}. - * - * @param source - * @return - * @throws IOException */ private JsonNode readTree(byte[] source) throws IOException { diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericToStringSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericToStringSerializer.java index a85d9d8e33..8159347b89 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericToStringSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericToStringSerializer.java @@ -92,6 +92,7 @@ public byte[] serialize(@Nullable T value) { } @Override + @Nullable public T deserialize(@Nullable byte[] bytes) { if (bytes == null) { diff --git a/src/main/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializer.java index daa55032a5..32d626bdd8 100644 --- a/src/main/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializer.java @@ -158,6 +158,7 @@ public byte[] serialize(@Nullable T value) throws SerializationException { } } + @Nullable @Override @SuppressWarnings("unchecked") public T deserialize(@Nullable byte[] bytes) throws SerializationException { diff --git a/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java index 9ed493346c..db67928a94 100644 --- a/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java @@ -84,6 +84,7 @@ public JdkSerializationRedisSerializer(Converter serializer, this.deserializer = deserializer; } + @Nullable @Override public byte[] serialize(@Nullable Object value) { @@ -98,6 +99,7 @@ public byte[] serialize(@Nullable Object value) { } } + @Nullable @Override public Object deserialize(@Nullable byte[] bytes) { diff --git a/src/main/java/org/springframework/data/redis/serializer/OxmSerializer.java b/src/main/java/org/springframework/data/redis/serializer/OxmSerializer.java index 7d98db1b64..18ceace01f 100644 --- a/src/main/java/org/springframework/data/redis/serializer/OxmSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/OxmSerializer.java @@ -102,6 +102,7 @@ public byte[] serialize(@Nullable Object value) throws SerializationException { return stream.toByteArray(); } + @Nullable @Override public Object deserialize(@Nullable byte[] bytes) throws SerializationException { diff --git a/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java index e8e5cd2209..b9fea3cb27 100644 --- a/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java @@ -80,11 +80,13 @@ public StringRedisSerializer(Charset charset) { this.charset = charset; } + @Nullable @Override public byte[] serialize(@Nullable String value) { return (value == null ? null : value.getBytes(charset)); } + @Nullable @Override public String deserialize(@Nullable byte[] bytes) { return (bytes == null ? null : new String(bytes, charset)); From 939af145eb585d0fc89d3fa76e8a9e4313615479 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 16 Jan 2025 14:03:05 +0100 Subject: [PATCH 126/187] Polishing. Remove tests for deprecated code. See #2561 --- .../ReactiveIntegrationTests.java | 81 ----------------- .../SynchronousIntegrationTests.java | 87 ------------------- .../lettuce/observability/TestConfig.java | 81 ----------------- 3 files changed, 249 deletions(-) delete mode 100644 src/test/java/org/springframework/data/redis/connection/lettuce/observability/ReactiveIntegrationTests.java delete mode 100644 src/test/java/org/springframework/data/redis/connection/lettuce/observability/SynchronousIntegrationTests.java delete mode 100644 src/test/java/org/springframework/data/redis/connection/lettuce/observability/TestConfig.java diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/ReactiveIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/observability/ReactiveIntegrationTests.java deleted file mode 100644 index 5c352df982..0000000000 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/ReactiveIntegrationTests.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection.lettuce.observability; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.connection.ReactiveRedisConnection; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.simple.SimpleMeterRegistry; -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationRegistry; -import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor; -import io.micrometer.tracing.test.SampleTestRunner; -import reactor.test.StepVerifier; -import reactor.util.context.Context; - -/** - * Collection of tests that log metrics and tracing using the reactive API. - * - * @author Mark Paluch - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = TestConfig.class) -public class ReactiveIntegrationTests extends SampleTestRunner { - - @Autowired LettuceConnectionFactory connectionFactory; - - ReactiveIntegrationTests() { - super(SampleRunnerConfig.builder().build()); - } - - @Override - protected MeterRegistry createMeterRegistry() { - return TestConfig.METER_REGISTRY; - } - - @Override - protected ObservationRegistry createObservationRegistry() { - return TestConfig.OBSERVATION_REGISTRY; - } - - @Override - public SampleTestRunnerConsumer yourCode() { - - return (tracer, meterRegistry) -> { - - Observation intermediate = Observation.start("intermediate", createObservationRegistry()); - - ReactiveRedisConnection connection = connectionFactory.getReactiveConnection(); - - connection.ping().contextWrite(Context.of(ObservationThreadLocalAccessor.KEY, intermediate)) - .as(StepVerifier::create).expectNext("PONG").verifyComplete(); - - intermediate.stop(); - - connection.close(); - - assertThat(tracer.getFinishedSpans()).isNotEmpty(); - System.out.println(((SimpleMeterRegistry) meterRegistry).getMetersAsString()); - }; - } -} diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/SynchronousIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/observability/SynchronousIntegrationTests.java deleted file mode 100644 index 5220a64fd4..0000000000 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/SynchronousIntegrationTests.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection.lettuce.observability; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.SettingsUtils; -import org.springframework.data.redis.connection.RedisConnection; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.simple.SimpleMeterRegistry; -import io.micrometer.observation.ObservationRegistry; -import io.micrometer.tracing.exporter.FinishedSpan; -import io.micrometer.tracing.test.SampleTestRunner; - -/** - * Collection of tests that log metrics and tracing using the synchronous API. - * - * @author Mark Paluch - * @author Yanming Zhou - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = TestConfig.class) -public class SynchronousIntegrationTests extends SampleTestRunner { - - @Autowired LettuceConnectionFactory connectionFactory; - - SynchronousIntegrationTests() { - super(SampleRunnerConfig.builder().build()); - } - - @Override - protected MeterRegistry createMeterRegistry() { - return TestConfig.METER_REGISTRY; - } - - @Override - protected ObservationRegistry createObservationRegistry() { - return TestConfig.OBSERVATION_REGISTRY; - } - - @Override - public SampleTestRunnerConsumer yourCode() { - - return (tracer, meterRegistry) -> { - - RedisConnection connection = connectionFactory.getConnection(); - connection.ping(); - - connection.close(); - - assertThat(tracer.getFinishedSpans()).isNotEmpty(); - System.out.println(((SimpleMeterRegistry) meterRegistry).getMetersAsString()); - - assertThat(tracer.getFinishedSpans()).isNotEmpty(); - - for (FinishedSpan finishedSpan : tracer.getFinishedSpans()) { - assertThat(finishedSpan.getTags()).containsEntry("db.system", "redis") - .containsEntry("net.sock.peer.addr", SettingsUtils.getHost()) - .containsEntry("net.sock.peer.port", "" + SettingsUtils.getPort()); - assertThat(finishedSpan.getTags()).containsKeys("db.operation"); - } - - assertThat(TestConfig.PARENT_OBSERVATION_NAMES_COLLECTED_IN_PREDICATE).isNotEmpty(); - TestConfig.PARENT_OBSERVATION_NAMES_COLLECTED_IN_PREDICATE.clear(); - }; - } - -} diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/TestConfig.java b/src/test/java/org/springframework/data/redis/connection/lettuce/observability/TestConfig.java deleted file mode 100644 index 387f799302..0000000000 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/observability/TestConfig.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection.lettuce.observability; - -import io.lettuce.core.resource.ClientResources; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler; -import io.micrometer.core.instrument.simple.SimpleMeterRegistry; -import io.micrometer.observation.ObservationRegistry; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.SettingsUtils; -import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.test.extension.ShutdownQueue; - -/** - * @author Mark Paluch - * @author Yanming Zhou - */ -@Configuration -class TestConfig { - - static final MeterRegistry METER_REGISTRY = new SimpleMeterRegistry(); - static final ObservationRegistry OBSERVATION_REGISTRY = ObservationRegistry.create(); - static final List PARENT_OBSERVATION_NAMES_COLLECTED_IN_PREDICATE = new ArrayList<>(); - - static { - OBSERVATION_REGISTRY.observationConfig().observationHandler(new DefaultMeterObservationHandler(METER_REGISTRY)); - OBSERVATION_REGISTRY.observationConfig().observationPredicate((name, context) -> { - if (context.getParentObservation() != null) { - PARENT_OBSERVATION_NAMES_COLLECTED_IN_PREDICATE.add(context.getParentObservation().getContextView().getName()); - } - return true; - }); - } - - @Bean(destroyMethod = "timer") - ClientResources clientResources(ObservationRegistry observationRegistry) { - - ClientResources resources = ClientResources.builder() - .tracing(new MicrometerTracingAdapter(observationRegistry, "Redis", true)).build(); - - ShutdownQueue.register(() -> resources.shutdown(0, 0, TimeUnit.MILLISECONDS)); - return resources; - } - - @Bean - LettuceConnectionFactory connectionFactory(ClientResources clientResources) { - - LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder() - .shutdownTimeout(Duration.ZERO).shutdownQuietPeriod(Duration.ZERO) - .clientResources(clientResources).build(); - - return new LettuceConnectionFactory(SettingsUtils.standaloneConfiguration(), clientConfiguration); - } - - @Bean - ObservationRegistry registry() { - return OBSERVATION_REGISTRY; - } -} From 1e40558ef27de8ac14aab716bcb23cd85683ac54 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 16 Jan 2025 14:29:42 +0100 Subject: [PATCH 127/187] Upgrade to Valkey 8.0.2. Close #3094 --- Jenkinsfile | 10 +++++----- Makefile | 2 +- .../Dockerfile | 0 ci/pipeline.properties | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) rename ci/{openjdk17-valkey-7.2 => openjdk17-valkey-8.0}/Dockerfile (100%) diff --git a/Jenkinsfile b/Jenkinsfile index 0344ab7349..a4820853f1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -60,10 +60,10 @@ pipeline { } } } - stage('Publish JDK 17 + Valkey 7.2 Docker Image') { + stage('Publish JDK 17 + Valkey 8.0 Docker Image') { when { anyOf { - changeset "ci/openjdk17-valkey-7.2/Dockerfile" + changeset "ci/openjdk17-valkey-8.0/Dockerfile" changeset "Makefile" changeset "ci/pipeline.properties" } @@ -73,7 +73,7 @@ pipeline { steps { script { - def image = docker.build("springci/spring-data-with-valkey-7.2:${p['java.main.tag']}", "--build-arg BASE=${p['docker.java.main.image']} --build-arg VERSION=${p['docker.redis.7.version']} -f ci/openjdk17-redis-7.2/Dockerfile .") + def image = docker.build("springci/spring-data-with-valkey-8.0:${p['java.main.tag']}", "--build-arg BASE=${p['docker.java.main.image']} --build-arg VERSION=${p['docker.valkey.8.version']} -f ci/openjdk17-valkey-8.0/Dockerfile .") docker.withRegistry(p['docker.registry'], p['docker.credentials']) { image.push() } @@ -197,7 +197,7 @@ pipeline { } } - stage("test: Valkey 7") { + stage("test: Valkey 8") { agent { label 'data' } @@ -209,7 +209,7 @@ pipeline { steps { script { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) { - docker.image("springci/spring-data-with-valkey-7.2:${p['java.main.tag']}").inside(p['docker.java.inside.docker']) { + docker.image("springci/spring-data-with-valkey-8.0:${p['java.main.tag']}").inside(p['docker.java.inside.docker']) { sh "PROFILE=none LONG_TESTS=true JENKINS_USER_NAME=${p['jenkins.user.name']} ci/test.sh" } } diff --git a/Makefile b/Makefile index 304a2648ba..d2051060c1 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION:=7.2.5 +VERSION?=7.2.5 PROJECT?=redis GH_ORG?=redis SPRING_PROFILE?=ci diff --git a/ci/openjdk17-valkey-7.2/Dockerfile b/ci/openjdk17-valkey-8.0/Dockerfile similarity index 100% rename from ci/openjdk17-valkey-7.2/Dockerfile rename to ci/openjdk17-valkey-8.0/Dockerfile diff --git a/ci/pipeline.properties b/ci/pipeline.properties index cd2fcf7fbe..7e44f69e06 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -16,6 +16,7 @@ docker.mongodb.8.0.version=8.0.0 # Supported versions of Redis docker.redis.6.version=6.2.13 docker.redis.7.version=7.2.4 +docker.valkey.8.version=8.0.2 # Supported versions of Cassandra docker.cassandra.3.version=3.11.16 From c3a5acea5b8aaf3c4a4e922bd0ed5acbe88479a1 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 21 Jan 2025 13:27:06 +0100 Subject: [PATCH 128/187] Disable flakey tests. See #3095 --- .../cache/DefaultRedisCacheWriterTests.java | 2 ++ .../StreamReceiverIntegrationTests.java | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java index 7df9bf8652..6b74b80931 100644 --- a/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java +++ b/src/test/java/org/springframework/data/redis/cache/DefaultRedisCacheWriterTests.java @@ -33,6 +33,7 @@ import java.util.function.Consumer; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -443,6 +444,7 @@ void noOpStatisticsCollectorReturnsEmptyStatsInstance() { } @ParameterizedRedisTest // GH-1686 + @Disabled("Occasional failures on CI but not locally") void doLockShouldGetLock() throws InterruptedException { int threadCount = 3; diff --git a/src/test/java/org/springframework/data/redis/stream/StreamReceiverIntegrationTests.java b/src/test/java/org/springframework/data/redis/stream/StreamReceiverIntegrationTests.java index ea5369856c..48cc363879 100644 --- a/src/test/java/org/springframework/data/redis/stream/StreamReceiverIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/stream/StreamReceiverIntegrationTests.java @@ -15,11 +15,13 @@ */ package org.springframework.data.redis.stream; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; import java.nio.ByteBuffer; import java.time.Duration; @@ -51,10 +53,6 @@ import org.springframework.data.redis.stream.StreamReceiver.StreamReceiverOptions; import org.springframework.data.redis.test.condition.EnabledOnCommand; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - /** * Integration tests for {@link StreamReceiver}. * @@ -227,13 +225,13 @@ void shouldReceiveAsConsumerGroupMessages() { .consumeNextWith(it -> { assertThat(it.getStream()).isEqualTo("my-stream"); - // assertThat(it.getValue()).containsEntry("key", "value"); - assertThat(it.getValue()).containsValue("value"); + + assertThat(it.getValue().values()).containsAnyOf("value", "value2"); }).consumeNextWith(it -> { assertThat(it.getStream()).isEqualTo("my-stream"); // assertThat(it.getValue()).containsEntry("key2", "value2"); - assertThat(it.getValue()).containsValue("value2"); + assertThat(it.getValue().values()).containsAnyOf("value", "value2"); }) // .thenCancel() // .verify(Duration.ofSeconds(5)); From 88333bd28677d14922e1df43b9cdfb6d99e2eb6e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 10 Feb 2025 11:43:24 +0100 Subject: [PATCH 129/187] Polishing. Reformat code. See #3024 --- .../redis/serializer/GenericJackson2JsonRedisSerializer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index a2f2fe258d..c2973aa627 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -157,7 +157,6 @@ public GenericJackson2JsonRedisSerializer(ObjectMapper mapper) { */ public GenericJackson2JsonRedisSerializer(ObjectMapper mapper, JacksonObjectReader reader, JacksonObjectWriter writer) { - this(mapper, reader, writer, null); } From d0508d0097785defd3822f277aeb2f29a1f64fa7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 11 Feb 2025 15:23:14 +0100 Subject: [PATCH 130/187] Update CI Properties. See #3045 --- .mvn/extensions.xml | 2 +- .mvn/jvm.config | 10 ++++++++++ ci/pipeline.properties | 1 - 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .mvn/jvm.config diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 1e3bb355f5..e0857eaa25 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -3,6 +3,6 @@ io.spring.develocity.conventions develocity-conventions-maven-extension - 0.0.19 + 0.0.22 diff --git a/.mvn/jvm.config b/.mvn/jvm.config new file mode 100644 index 0000000000..32599cefea --- /dev/null +++ b/.mvn/jvm.config @@ -0,0 +1,10 @@ +--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED +--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED +--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 7e44f69e06..cd2fcf7fbe 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -16,7 +16,6 @@ docker.mongodb.8.0.version=8.0.0 # Supported versions of Redis docker.redis.6.version=6.2.13 docker.redis.7.version=7.2.4 -docker.valkey.8.version=8.0.2 # Supported versions of Cassandra docker.cassandra.3.version=3.11.16 From 1b48a756f95c5c12e4e64a3c0f40ed2a8efdf8ea Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Feb 2025 12:22:19 +0100 Subject: [PATCH 131/187] Prepare 3.5 M1 (2025.0.0). See #3045 --- pom.xml | 16 +++------------- src/main/resources/notice.txt | 3 ++- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index fd73e23ebd..079aee2da9 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.5.0-SNAPSHOT + 3.5.0-M1 - 3.5.0-SNAPSHOT - 3.5.0-SNAPSHOT + 3.5.0-M1 + 3.5.0-M1 4.0.2 1.9.4 1.4.21 @@ -389,16 +389,6 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - spring-milestone https://repo.spring.io/milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 66c328a8a4..98fe9a6e5e 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 3.4 GA (2024.1.0) +Spring Data Redis 3.5 M1 (2025.0.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -56,5 +56,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From bba21f21a730a0a8da51073ade2436531d58afd2 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Feb 2025 12:22:37 +0100 Subject: [PATCH 132/187] Release version 3.5 M1 (2025.0.0). See #3045 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 079aee2da9..de0f689f66 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.5.0-SNAPSHOT + 3.5.0-M1 Spring Data Redis Spring Data module for Redis From 415cf1a8163e1ec1306fe1470b592c425562ee34 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Feb 2025 12:25:07 +0100 Subject: [PATCH 133/187] Prepare next development iteration. See #3045 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index de0f689f66..079aee2da9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.5.0-M1 + 3.5.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From ea4d7280e2e4a278f95942256f0acbb0ff3d53fc Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Feb 2025 12:25:08 +0100 Subject: [PATCH 134/187] After release cleanups. See #3045 --- pom.xml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 079aee2da9..fd73e23ebd 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.5.0-M1 + 3.5.0-SNAPSHOT - 3.5.0-M1 - 3.5.0-M1 + 3.5.0-SNAPSHOT + 3.5.0-SNAPSHOT 4.0.2 1.9.4 1.4.21 @@ -389,6 +389,16 @@ + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + spring-milestone https://repo.spring.io/milestone From cdf9dc360cd727b88e93d32d2d4943b66ffe6304 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 10 Oct 2024 15:24:43 +0200 Subject: [PATCH 135/187] =?UTF-8?q?Adds=20support=20for=20Redis=20`SET=20?= =?UTF-8?q?=E2=80=A6=20GET`=20command.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2853 Original pull request: #3017 --- .../DefaultStringRedisConnection.java | 5 +++ .../connection/DefaultedRedisConnection.java | 7 ++++ .../connection/ReactiveStringCommands.java | 36 +++++++++++++++++++ .../redis/connection/RedisStringCommands.java | 16 +++++++++ .../jedis/JedisClusterStringCommands.java | 18 ++++++++++ .../connection/jedis/JedisStringCommands.java | 14 ++++++++ .../LettuceReactiveStringCommands.java | 13 +++++++ .../lettuce/LettuceStringCommands.java | 12 +++++++ .../data/redis/core/BoundValueOperations.java | 13 +++++++ .../core/DefaultReactiveValueOperations.java | 12 +++++++ .../redis/core/DefaultValueOperations.java | 21 +++++++++++ .../redis/core/ReactiveValueOperations.java | 12 +++++++ .../data/redis/core/ValueOperations.java | 28 +++++++++++++++ .../connection/RedisConnectionUnitTests.java | 5 +++ ...eactiveStringCommandsIntegrationTests.java | 25 +++++++++++++ ...efaultValueOperationsIntegrationTests.java | 26 ++++++++++++++ .../core/RedisTemplateIntegrationTests.java | 17 +++++++++ 17 files changed, 280 insertions(+) diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java index 8fe2f2c9f7..3520b682ea 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -770,6 +770,11 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op return convertAndReturn(delegate.set(key, value, expiration, option), Converters.identityConverter()); } + @Override + public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) { + return convertAndReturn(delegate.setGet(key, value, expiration, option), Converters.identityConverter()); + } + @Override public Boolean setBit(byte[] key, long offset, boolean value) { return convertAndReturn(delegate.setBit(key, offset, value), Converters.identityConverter()); diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java index aa5f6de773..c56ec98306 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -326,6 +326,13 @@ default Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption o return stringCommands().set(key, value, expiration, option); } + /** @deprecated in favor of {@link RedisConnection#stringCommands()}}. */ + @Override + @Deprecated + default byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) { + return stringCommands().setGet(key, value, expiration, option); + } + /** @deprecated in favor of {@link RedisConnection#stringCommands()}}. */ @Override @Deprecated diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java index ab324fb46e..1bce0b224f 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java @@ -47,6 +47,7 @@ * * @author Christoph Strobl * @author Mark Paluch + * @author Marcin Grzejszczak * @since 2.0 */ public interface ReactiveStringCommands { @@ -193,6 +194,41 @@ default Mono set(ByteBuffer key, ByteBuffer value, Expiration expiratio */ Flux> set(Publisher commands); + /** + * Set {@literal value} for {@literal key} with {@literal expiration} and {@literal options}. Return the old + * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value + * stored at key is not a string. + * + * @param key must not be {@literal null}. + * @param value must not be {@literal null}. + * @param expiration must not be {@literal null}. Use {@link Expiration#persistent()} for no expiration time or + * {@link Expiration#keepTtl()} to keep the existing. + * @param option must not be {@literal null}. + * @return + * @see Redis Documentation: SET + * @since 3.4 + */ + @Nullable + default Mono setGet(ByteBuffer key, ByteBuffer value, Expiration expiration, SetOption option) { + + Assert.notNull(key, "Key must not be null"); + Assert.notNull(value, "Value must not be null"); + + return setGet(Mono.just(SetCommand.set(key).value(value).withSetOption(option).expiring(expiration))).next() + .map(CommandResponse::getOutput); + } + + /** + * Set each and every item separately by invoking {@link SetCommand}. Return the old + * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value + * stored at key is not a string. + * + * @param commands must not be {@literal null}. + * @return {@link Flux} of {@link ByteBufferResponse} holding the {@link SetCommand} along with the command result. + * @see Redis Documentation: SET + */ + Flux> setGet(Publisher commands); + /** * Get single element stored at {@literal key}. * diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java index 007bbc774c..39ce9f2fd2 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java @@ -122,6 +122,22 @@ enum BitOperation { @Nullable Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option); + /** + * Set {@code value} for {@code key}. Return the old string stored at key, or nil if key did not exist. + * An error is returned and SET aborted if the value stored at key is not a string. + * + * @param key must not be {@literal null}. + * @param value must not be {@literal null}. + * @param expiration must not be {@literal null}. Use {@link Expiration#persistent()} to not set any ttl or + * {@link Expiration#keepTtl()} to keep the existing expiration. + * @param option must not be {@literal null}. Use {@link SetOption#upsert()} to add non existing. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.4 + * @see Redis Documentation: SET + */ + @Nullable + byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option); + /** * Set {@code value} for {@code key}, only if {@code key} does not exist. * 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 ba02de01a0..1752039714 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 @@ -150,6 +150,24 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op } } + @Override + public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) { + + Assert.notNull(key, "Key must not be null"); + Assert.notNull(value, "Value must not be null"); + Assert.notNull(expiration, "Expiration must not be null"); + Assert.notNull(option, "Option must not be null"); + + SetParams setParams = JedisConverters.toSetCommandExPxArgument(expiration, + JedisConverters.toSetCommandNxXxArgument(option)); + + try { + return connection.getCluster().setGet(key, value, setParams); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + @Override public Boolean setNX(byte[] key, byte[] value) { diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java index 9b21e8e715..088c71c361 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java @@ -116,6 +116,20 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op .getOrElse(Converters.stringToBooleanConverter(), () -> false); } + @Override + @Nullable + public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(value, "Value must not be null"); + Assert.notNull(expiration, "Expiration must not be null"); + Assert.notNull(option, "Option must not be null"); + + SetParams params = JedisConverters.toSetCommandExPxArgument(expiration, + JedisConverters.toSetCommandNxXxArgument(option)); + + return connection.invoke().just(Jedis::setGet, PipelineBinaryCommands::setGet, key, value, params); + } + @Override public Boolean setNX(byte[] key, byte[] value) { diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java index eaec33cb20..08d2972b52 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java @@ -105,6 +105,19 @@ public Flux> set(Publisher commands) { })); } + @Override + public Flux> setGet(Publisher commands) { + return this.connection.execute(reactiveCommands -> Flux.from(commands).concatMap((command) -> { + + Assert.notNull(command.getKey(), "Key must not be null"); + Assert.notNull(command.getValue(), "Value must not be null"); + + return reactiveCommands.setGet(command.getKey(), command.getValue()) + .map(v -> new ByteBufferResponse<>(command, v)) + .defaultIfEmpty(new AbsentByteBufferResponse<>(command)); + })); + } + @Override public Flux> getSet(Publisher commands) { diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java index ebaeb7475f..c4c38a41e7 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java @@ -115,6 +115,18 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op .orElse(LettuceConverters.stringToBooleanConverter(), false); } + @Override + @Nullable + public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(value, "Value must not be null"); + Assert.notNull(expiration, "Expiration must not be null"); + Assert.notNull(option, "Option must not be null"); + + return connection.invoke() + .just(RedisStringAsyncCommands::setGet, key, value, LettuceConverters.toSetArgs(expiration, option)); + } + @Override public Boolean setNX(byte[] key, byte[] value) { diff --git a/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java b/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java index 40be5358f8..53b873334e 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java @@ -49,6 +49,19 @@ public interface BoundValueOperations extends BoundKeyOperations { */ void set(V value, long timeout, TimeUnit unit); + /** + * Set the {@code value} and expiration {@code timeout} for the bound key. Return the old + * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value + * stored at key is not a string. + * + * @param value must not be {@literal null}. + * @param timeout + * @param unit must not be {@literal null}. + * @see Redis Documentation: SET + * @since 3.4 + */ + V setGet(V value, long timeout, TimeUnit unit); + /** * Set the {@code value} and expiration {@code timeout} for the bound key. * diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveValueOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveValueOperations.java index 4ed0483330..8cf8d54985 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveValueOperations.java @@ -77,6 +77,18 @@ public Mono set(K key, V value, Duration timeout) { stringCommands.set(rawKey(key), rawValue(value), Expiration.from(timeout), SetOption.UPSERT)); } + @Override + public Mono setGet(K key, V value, Duration timeout) { + + Assert.notNull(key, "Key must not be null"); + Assert.notNull(value, "Value must not be null"); + Assert.notNull(timeout, "Duration must not be null"); + + return createMono(stringCommands -> + stringCommands.setGet(rawKey(key), rawValue(value), Expiration.from(timeout), SetOption.UPSERT)) + .map(this::readRequiredValue); + } + @Override public Mono setIfAbsent(K key, V value) { diff --git a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java index cbc44ca3c0..b20e570f05 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java @@ -208,6 +208,27 @@ public void set(K key, V value, long timeout, TimeUnit unit) { execute(connection -> connection.set(rawKey, rawValue, Expiration.from(timeout, unit), SetOption.upsert())); } + @Override + public V setGet(K key, V value, long timeout, TimeUnit unit) { + return doSetGet(key, value, Expiration.from(timeout, unit)); + } + + @Override + public V setGet(K key, V value, Duration duration) { + return doSetGet(key, value, Expiration.from(duration)); + } + + private V doSetGet(K key, V value, Expiration duration) { + byte[] rawValue = rawValue(value); + return execute( new ValueDeserializingRedisCallback(key) { + + @Override + protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { + return connection.stringCommands().setGet(rawKey, rawValue, duration, SetOption.UPSERT); + } + }); + } + @Override public Boolean setIfAbsent(K key, V value) { diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java index 123376c1d2..3eab4929d6 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java @@ -58,6 +58,18 @@ public interface ReactiveValueOperations { */ Mono set(K key, V value, Duration timeout); + /** + * Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old + * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value + * stored at key is not a string. + * + * @param key must not be {@literal null}. + * @param value + * @param timeout must not be {@literal null}. + * @see Redis Documentation: SETEX + */ + Mono setGet(K key, V value, Duration timeout); + /** * Set {@code key} to hold the string {@code value} if {@code key} is absent. * diff --git a/src/main/java/org/springframework/data/redis/core/ValueOperations.java b/src/main/java/org/springframework/data/redis/core/ValueOperations.java index e3abd0918f..ac4ac84ad3 100644 --- a/src/main/java/org/springframework/data/redis/core/ValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ValueOperations.java @@ -32,6 +32,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author Jiahe Cai + * @author Marcin Grzejszczak */ public interface ValueOperations { @@ -44,6 +45,33 @@ public interface ValueOperations { */ void set(K key, V value); + /** + * Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old + * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value + * stored at key is not a string. + * + * @param key must not be {@literal null}. + * @param value must not be {@literal null}. + * @param timeout the key expiration timeout. + * @param unit must not be {@literal null}. + * @see Redis Documentation: SET + * @since 3.4 + */ + V setGet(K key, V value, long timeout, TimeUnit unit); + + /** + * Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old + * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value + * stored at key is not a string. + * + * @param key must not be {@literal null}. + * @param value must not be {@literal null}. + * @param duration expiration duration + * @see Redis Documentation: SET + * @since 3.4 + */ + V setGet(K key, V value, Duration duration); + /** * Set the {@code value} and expiration {@code timeout} for {@code key}. * diff --git a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java index d66c7057ce..068e87444a 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java @@ -1093,6 +1093,11 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op return delegate.set(key, value, expiration, options); } + @Override + public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) { + return delegate.setGet(key, value, expiration, option); + } + @Override public List bitField(byte[] key, BitFieldSubCommands subCommands) { return delegate.bitField(key, subCommands); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java index af3e43e135..1bb099161c 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java @@ -556,4 +556,29 @@ void setKeepTTL() { assertThat(nativeBinaryCommands.ttl(KEY_1_BBUFFER)).isCloseTo(expireSeconds, Offset.offset(5L)); assertThat(nativeCommands.get(KEY_1)).isEqualTo(VALUE_2); } + + @ParameterizedRedisTest // GH-2853 + void setGetMono() { + nativeCommands.set(KEY_1, VALUE_1); + + connection.stringCommands().setGet(KEY_1_BBUFFER, VALUE_2_BBUFFER, Expiration.keepTtl(), SetOption.upsert()) + .as(StepVerifier::create) // + .expectNext(VALUE_1_BBUFFER) // + .verifyComplete(); + + assertThat(nativeCommands.get(KEY_1)).isEqualTo(VALUE_2); + } + + @ParameterizedRedisTest // GH-2853 + void setGetFlux() { + nativeCommands.set(KEY_1, VALUE_1); + + connection.stringCommands().setGet(Mono.just(SetCommand.set(KEY_1_BBUFFER).value(VALUE_2_BBUFFER).expiring(Expiration.keepTtl()).withSetOption( SetOption.upsert()))) + .map(CommandResponse::getOutput) + .as(StepVerifier::create) // + .expectNext(VALUE_1_BBUFFER) // + .verifyComplete(); + + assertThat(nativeCommands.get(KEY_1)).isEqualTo(VALUE_2); + } } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsIntegrationTests.java index 1d73298e8d..abc601bb8d 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultValueOperationsIntegrationTests.java @@ -316,6 +316,32 @@ void testSetWithExpirationWithTimeUnitMilliseconds() { await().atMost(Duration.ofMillis(500L)).until(() -> !redisTemplate.hasKey(key)); } + @ParameterizedRedisTest + void testSetGetWithExpiration() { + + K key = keyFactory.instance(); + V value1 = valueFactory.instance(); + V value2 = valueFactory.instance(); + + valueOps.set(key, value1); + + assertThat(valueOps.setGet(key, value2, 1, TimeUnit.SECONDS)).isEqualTo(value1); + assertThat(valueOps.get(key)).isEqualTo(value2); + } + + @ParameterizedRedisTest + void testSetGetWithExpirationDuration() { + + K key = keyFactory.instance(); + V value1 = valueFactory.instance(); + V value2 = valueFactory.instance(); + + valueOps.set(key, value1); + + assertThat(valueOps.setGet(key, value2, Duration.ofMillis(1000))).isEqualTo(value1); + assertThat(valueOps.get(key)).isEqualTo(value2); + } + @ParameterizedRedisTest void testAppend() { diff --git a/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java index de22a409c4..2bb8046f87 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java @@ -571,6 +571,23 @@ void testGetExpireMillis() { assertThat(ttl).isLessThan(25L); } + @ParameterizedRedisTest // GH-3017 + void testSetGetExpireMillis() { + + K key = keyFactory.instance(); + V value1 = valueFactory.instance(); + V value2 = valueFactory.instance(); + redisTemplate.boundValueOps(key).set(value1); + + V oldValue = redisTemplate.boundValueOps(key).setGet(value2, 1, TimeUnit.DAYS); + redisTemplate.expire(key, 1, TimeUnit.DAYS); + Long ttl = redisTemplate.getExpire(key, TimeUnit.HOURS); + + assertThat(oldValue).isEqualTo(value1); + assertThat(ttl).isGreaterThanOrEqualTo(23L); + assertThat(ttl).isLessThan(25L); + } + @ParameterizedRedisTest // DATAREDIS-611 void testGetExpireDuration() { From 5e71dbeb606d443bb9bd32ad3f363ba2a1a44df9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 24 Feb 2025 09:47:21 +0100 Subject: [PATCH 136/187] Polishing. Reformat code, add since tags. Tweak Javadoc wording. See #2853 Original pull request: #3017 --- .../connection/ReactiveStringCommands.java | 14 +++++------ .../redis/connection/RedisStringCommands.java | 9 ++++---- .../jedis/JedisClusterStringCommands.java | 1 + .../connection/jedis/JedisStringCommands.java | 2 ++ .../LettuceReactiveStringCommands.java | 2 ++ .../lettuce/LettuceStringCommands.java | 2 ++ .../data/redis/core/BoundValueOperations.java | 23 +++++++++++++++---- .../redis/core/DefaultValueOperations.java | 4 +++- .../redis/core/ReactiveValueOperations.java | 6 ++--- .../data/redis/core/ValueOperations.java | 18 ++++++++------- ...eactiveStringCommandsIntegrationTests.java | 3 +++ 11 files changed, 57 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java index 1bce0b224f..3c1bfc8eea 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java @@ -195,9 +195,9 @@ default Mono set(ByteBuffer key, ByteBuffer value, Expiration expiratio Flux> set(Publisher commands); /** - * Set {@literal value} for {@literal key} with {@literal expiration} and {@literal options}. Return the old - * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value - * stored at key is not a string. + * Set {@literal value} for {@literal key} with {@literal expiration} and {@literal options}. Return the old string + * stored at key, or empty if key did not exist. An error is returned and SET aborted if the value stored at key is + * not a string. * * @param key must not be {@literal null}. * @param value must not be {@literal null}. @@ -206,7 +206,7 @@ default Mono set(ByteBuffer key, ByteBuffer value, Expiration expiratio * @param option must not be {@literal null}. * @return * @see Redis Documentation: SET - * @since 3.4 + * @since 3.5 */ @Nullable default Mono setGet(ByteBuffer key, ByteBuffer value, Expiration expiration, SetOption option) { @@ -219,13 +219,13 @@ default Mono setGet(ByteBuffer key, ByteBuffer value, Expiration exp } /** - * Set each and every item separately by invoking {@link SetCommand}. Return the old - * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value - * stored at key is not a string. + * Set each and every item separately by invoking {@link SetCommand}. Return the old string stored at key, or empty if + * key did not exist. An error is returned and SET aborted if the value stored at key is not a string. * * @param commands must not be {@literal null}. * @return {@link Flux} of {@link ByteBufferResponse} holding the {@link SetCommand} along with the command result. * @see Redis Documentation: SET + * @since 3.5 */ Flux> setGet(Publisher commands); diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java index 39ce9f2fd2..f591be4245 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java @@ -29,6 +29,7 @@ * @author Costin Leau * @author Christoph Strobl * @author Mark Paluch + * @author Marcin Grzejszczak */ public interface RedisStringCommands { @@ -123,16 +124,16 @@ enum BitOperation { Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option); /** - * Set {@code value} for {@code key}. Return the old string stored at key, or nil if key did not exist. - * An error is returned and SET aborted if the value stored at key is not a string. + * Set {@code value} for {@code key}. Return the old string stored at key, or {@literal null} if key did not exist. An + * error is returned and SET aborted if the value stored at key is not a string. * * @param key must not be {@literal null}. * @param value must not be {@literal null}. * @param expiration must not be {@literal null}. Use {@link Expiration#persistent()} to not set any ttl or * {@link Expiration#keepTtl()} to keep the existing expiration. - * @param option must not be {@literal null}. Use {@link SetOption#upsert()} to add non existing. + * @param option must not be {@literal null}. Use {@link SetOption#upsert()} to add non-existing. * @return {@literal null} when used in pipeline / transaction. - * @since 3.4 + * @since 3.5 * @see Redis Documentation: SET */ @Nullable 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 1752039714..af51cafe3d 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 @@ -42,6 +42,7 @@ * @author Mark Paluch * @author Xiaohu Zhang * @author dengliming + * @author Marcin Grzejszczak * @since 2.0 */ class JedisClusterStringCommands implements RedisStringCommands { diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java index 088c71c361..392591e36a 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisStringCommands.java @@ -35,6 +35,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author dengliming + * @author Marcin Grzejszczak * @since 2.0 */ class JedisStringCommands implements RedisStringCommands { @@ -119,6 +120,7 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op @Override @Nullable public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) { + Assert.notNull(key, "Key must not be null"); Assert.notNull(value, "Value must not be null"); Assert.notNull(expiration, "Expiration must not be null"); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java index 08d2972b52..9bb93f3987 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java @@ -48,6 +48,7 @@ * @author Jiahe Cai * @author Michele Mancioppi * @author John Blum + * @author Marcin Grzejszczak * @since 2.0 */ class LettuceReactiveStringCommands implements ReactiveStringCommands { @@ -107,6 +108,7 @@ public Flux> set(Publisher commands) { @Override public Flux> setGet(Publisher commands) { + return this.connection.execute(reactiveCommands -> Flux.from(commands).concatMap((command) -> { Assert.notNull(command.getKey(), "Key must not be null"); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java index c4c38a41e7..5dad73a71e 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java @@ -36,6 +36,7 @@ * @author Mark Paluch * @author dengliming * @author John Blum + * @author Marcin Grzejszczak * @since 2.0 */ class LettuceStringCommands implements RedisStringCommands { @@ -118,6 +119,7 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op @Override @Nullable public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) { + Assert.notNull(key, "Key must not be null"); Assert.notNull(value, "Value must not be null"); Assert.notNull(expiration, "Expiration must not be null"); diff --git a/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java b/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java index 53b873334e..429b053ff4 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundValueOperations.java @@ -28,6 +28,7 @@ * @author Mark Paluch * @author Jiahe Cai * @author Christoph Strobl + * @author Marcin Grzejszczak */ public interface BoundValueOperations extends BoundKeyOperations { @@ -50,18 +51,32 @@ public interface BoundValueOperations extends BoundKeyOperations { void set(V value, long timeout, TimeUnit unit); /** - * Set the {@code value} and expiration {@code timeout} for the bound key. Return the old - * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value - * stored at key is not a string. + * Set the {@code value} and expiration {@code timeout} for the bound key. Return the old string stored at key, or + * {@literal null} if key did not exist. An error is returned and SET aborted if the value stored at key is not a + * string. * * @param value must not be {@literal null}. * @param timeout * @param unit must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. * @see Redis Documentation: SET - * @since 3.4 + * @since 3.5 */ V setGet(V value, long timeout, TimeUnit unit); + /** + * Set the {@code value} and expiration {@code timeout} for the bound key. Return the old string stored at key, or + * {@literal null} if key did not exist. An error is returned and SET aborted if the value stored at key is not a + * string. + * + * @param value must not be {@literal null}. + * @param duration expiration duration + * @return {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: SET + * @since 3.5 + */ + V setGet(V value, Duration duration); + /** * Set the {@code value} and expiration {@code timeout} for the bound key. * diff --git a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java index b20e570f05..357dda961f 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultValueOperations.java @@ -25,6 +25,7 @@ import org.springframework.data.redis.connection.BitFieldSubCommands; import org.springframework.data.redis.connection.DefaultedRedisConnection; +import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisStringCommands.SetOption; import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; @@ -219,8 +220,9 @@ public V setGet(K key, V value, Duration duration) { } private V doSetGet(K key, V value, Expiration duration) { + byte[] rawValue = rawValue(value); - return execute( new ValueDeserializingRedisCallback(key) { + return execute(new ValueDeserializingRedisCallback(key) { @Override protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java index 3eab4929d6..9eff052005 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveValueOperations.java @@ -59,14 +59,14 @@ public interface ReactiveValueOperations { Mono set(K key, V value, Duration timeout); /** - * Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old - * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value - * stored at key is not a string. + * Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old string stored at key, or empty + * if key did not exist. An error is returned and SET aborted if the value stored at key is not a string. * * @param key must not be {@literal null}. * @param value * @param timeout must not be {@literal null}. * @see Redis Documentation: SETEX + * @since 3.5 */ Mono setGet(K key, V value, Duration timeout); diff --git a/src/main/java/org/springframework/data/redis/core/ValueOperations.java b/src/main/java/org/springframework/data/redis/core/ValueOperations.java index ac4ac84ad3..84ce00f385 100644 --- a/src/main/java/org/springframework/data/redis/core/ValueOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ValueOperations.java @@ -46,29 +46,31 @@ public interface ValueOperations { void set(K key, V value); /** - * Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old - * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value - * stored at key is not a string. + * Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old string stored at key, or + * {@literal null} if key did not exist. An error is returned and SET aborted if the value stored at key is not a + * string. * * @param key must not be {@literal null}. * @param value must not be {@literal null}. * @param timeout the key expiration timeout. * @param unit must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. * @see Redis Documentation: SET - * @since 3.4 + * @since 3.5 */ V setGet(K key, V value, long timeout, TimeUnit unit); /** - * Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old - * string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value - * stored at key is not a string. + * Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old string stored at key, or + * {@literal null} if key did not exist. An error is returned and SET aborted if the value stored at key is not a + * string. * * @param key must not be {@literal null}. * @param value must not be {@literal null}. * @param duration expiration duration + * @return {@literal null} when used in pipeline / transaction. * @see Redis Documentation: SET - * @since 3.4 + * @since 3.5 */ V setGet(K key, V value, Duration duration); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java index 1bb099161c..530ec229b9 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java @@ -559,6 +559,7 @@ void setKeepTTL() { @ParameterizedRedisTest // GH-2853 void setGetMono() { + nativeCommands.set(KEY_1, VALUE_1); connection.stringCommands().setGet(KEY_1_BBUFFER, VALUE_2_BBUFFER, Expiration.keepTtl(), SetOption.upsert()) @@ -571,6 +572,7 @@ void setGetMono() { @ParameterizedRedisTest // GH-2853 void setGetFlux() { + nativeCommands.set(KEY_1, VALUE_1); connection.stringCommands().setGet(Mono.just(SetCommand.set(KEY_1_BBUFFER).value(VALUE_2_BBUFFER).expiring(Expiration.keepTtl()).withSetOption( SetOption.upsert()))) @@ -581,4 +583,5 @@ void setGetFlux() { assertThat(nativeCommands.get(KEY_1)).isEqualTo(VALUE_2); } + } From 50ea34080c5e44099ae7b6b9deaf75f83f96f79c Mon Sep 17 00:00:00 2001 From: Tihomir Mateev Date: Mon, 25 Nov 2024 13:58:32 +0200 Subject: [PATCH 137/187] Add support for Hash Field Expiration. Signed-off-by: Tihomir Mateev Closes: #3054 --- Makefile | 2 +- .../DefaultStringRedisConnection.java | 70 +++ .../connection/DefaultedRedisConnection.java | 50 ++ .../connection/ReactiveHashCommands.java | 511 +++++++++++++++++- .../redis/connection/RedisHashCommands.java | 109 ++++ .../connection/StringRedisConnection.java | 107 ++++ .../jedis/JedisClusterHashCommands.java | 88 +++ .../connection/jedis/JedisHashCommands.java | 38 ++ .../lettuce/LettuceHashCommands.java | 37 ++ .../lettuce/LettuceReactiveHashCommands.java | 84 +++ .../data/redis/core/BoundHashOperations.java | 76 +++ .../redis/core/DefaultHashOperations.java | 45 ++ .../data/redis/core/HashOperations.java | 80 +++ .../support/collections/DefaultRedisMap.java | 30 + .../redis/support/collections/RedisMap.java | 73 +++ .../support/collections/RedisProperties.java | 43 +- .../AbstractConnectionIntegrationTests.java | 189 +++++++ .../jedis/JedisClusterConnectionTests.java | 144 +++++ .../LettuceClusterConnectionTests.java | 143 +++++ ...eReactiveHashCommandsIntegrationTests.java | 60 ++ ...DefaultHashOperationsIntegrationTests.java | 80 +++ .../AbstractRedisMapIntegrationTests.java | 33 ++ 22 files changed, 2052 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index d2051060c1..1f6dee240f 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION?=7.2.5 +VERSION?=7.4.0 PROJECT?=redis GH_ORG?=redis SPRING_PROFILE?=ci diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java index 3520b682ea..dd03f14bc5 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -2571,6 +2571,76 @@ public Long hStrLen(byte[] key, byte[] field) { return convertAndReturn(delegate.hStrLen(key, field), Converters.identityConverter()); } + @Override + public List hExpire(byte[] key, long seconds, byte[]... fields) { + return this.delegate.hExpire(key, seconds, fields); + } + + @Override + public List hpExpire(byte[] key, long millis, byte[]... fields) { + return this.delegate.hpExpire(key, millis, fields); + } + + @Override + public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { + return this.delegate.hExpireAt(key, unixTime, fields); + } + + @Override + public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { + return this.delegate.hpExpireAt(key, unixTimeInMillis, fields); + } + + @Override + public List hPersist(byte[] key, byte[]... fields) { + return this.delegate.hPersist(key, fields); + } + + @Override + public List hTtl(byte[] key, byte[]... fields) { + return this.delegate.hTtl(key, fields); + } + + @Override + public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { + return this.delegate.hTtl(key, timeUnit, fields); + } + + @Override + public List hExpire(String key, long seconds, String... fields) { + return hExpire(serialize(key), seconds, serializeMulti(fields)); + } + + @Override + public List hpExpire(String key, long millis, String... fields) { + return hpExpire(serialize(key), millis, serializeMulti(fields)); + } + + @Override + public List hExpireAt(String key, long unixTime, String... fields) { + return hExpireAt(serialize(key), unixTime, serializeMulti(fields)); + } + + @Override + public List hpExpireAt(String key, long unixTimeInMillis, String... fields) { + return hpExpireAt(serialize(key), unixTimeInMillis, serializeMulti(fields)); + } + + @Override + public List hPersist(String key, String... fields) { + return hPersist(serialize(key), serializeMulti(fields)); + } + + @Override + public List hTtl(String key, String... fields) { + return hTtl(serialize(key), serializeMulti(fields)); + } + + @Override + public List hTtl(String key, TimeUnit timeUnit, String... fields) { + return hTtl(serialize(key), timeUnit, serializeMulti(fields)); + } + @Override public void setClientName(byte[] name) { this.delegate.setClientName(name); diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java index c56ec98306..3a83343adb 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -65,6 +65,7 @@ * @author ihaohong * @author Dennis Neufeld * @author Shyngys Sapraliyev + * @author Tihomir Mateev * @since 2.0 */ @Deprecated @@ -1477,6 +1478,55 @@ default Long hStrLen(byte[] key, byte[] field) { return hashCommands().hStrLen(key, field); } + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hExpire(byte[] key, long seconds, byte[]... fields) { + return hashCommands().hExpire(key, seconds, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hpExpire(byte[] key, long millis, byte[]... fields) { + return hashCommands().hpExpire(key, millis, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hExpireAt(byte[] key, long unixTime, byte[]... fields) { + return hashCommands().hExpireAt(key, unixTime, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { + return hashCommands().hpExpireAt(key, unixTimeInMillis, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hPersist(byte[] key, byte[]... fields) { + return hashCommands().hPersist(key, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hTtl(byte[] key, byte[]... fields) { + return hashCommands().hTtl(key, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { + return hashCommands().hTtl(key, timeUnit, fields); + } + // GEO COMMANDS /** @deprecated in favor of {@link RedisConnection#geoCommands()}}. */ diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java index 0fae8d30b8..35e3437141 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java @@ -19,6 +19,8 @@ import reactor.core.publisher.Mono; import java.nio.ByteBuffer; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -44,10 +46,34 @@ * * @author Christoph Strobl * @author Mark Paluch + * @author Tihomir Mateev * @since 2.0 */ public interface ReactiveHashCommands { + /** + * {@link Command} for hash-bound operations. + * + * @author Christoph Strobl + * @author Tihomir Mateev + */ + class HashFieldsCommand extends KeyCommand { + + private final List fields; + + private HashFieldsCommand(@Nullable ByteBuffer key, List fields) { + super(key); + this.fields = fields; + } + + /** + * @return never {@literal null}. + */ + public List getFields() { + return fields; + } + } + /** * {@literal HSET} {@link Command}. * @@ -216,15 +242,10 @@ default Mono hMSet(ByteBuffer key, Map fieldVal * @author Christoph Strobl * @see Redis Documentation: HGET */ - class HGetCommand extends KeyCommand { - - private List fields; + class HGetCommand extends HashFieldsCommand { private HGetCommand(@Nullable ByteBuffer key, List fields) { - - super(key); - - this.fields = fields; + super(key, fields); } /** @@ -263,14 +284,7 @@ public HGetCommand from(ByteBuffer key) { Assert.notNull(key, "Key must not be null"); - return new HGetCommand(key, fields); - } - - /** - * @return never {@literal null}. - */ - public List getFields() { - return fields; + return new HGetCommand(key, getFields()); } } @@ -394,15 +408,10 @@ default Mono hExists(ByteBuffer key, ByteBuffer field) { * @author Christoph Strobl * @see Redis Documentation: HDEL */ - class HDelCommand extends KeyCommand { - - private final List fields; + class HDelCommand extends HashFieldsCommand { private HDelCommand(@Nullable ByteBuffer key, List fields) { - - super(key); - - this.fields = fields; + super(key, fields); } /** @@ -441,14 +450,7 @@ public HDelCommand from(ByteBuffer key) { Assert.notNull(key, "Key must not be null"); - return new HDelCommand(key, fields); - } - - /** - * @return never {@literal null}. - */ - public List getFields() { - return fields; + return new HDelCommand(key, getFields()); } } @@ -842,4 +844,453 @@ default Mono hStrLen(ByteBuffer key, ByteBuffer field) { * @since 2.1 */ Flux> hStrLen(Publisher commands); + + /** + * @author Tihomir Mateev + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + class Expire extends HashFieldsCommand { + + private final Duration ttl; + + /** + * Creates a new {@link Expire} given a {@code key}, a {@link List} of {@code fields} and a time-to-live + * + * @param key can be {@literal null}. + * @param fields must not be {@literal null}. + * @param ttl the duration of the time to live. + */ + private Expire(@Nullable ByteBuffer key, List fields, Duration ttl) { + + super(key, fields); + this.ttl = ttl; + } + + /** + * Specify the {@code fields} within the hash to set an expiration for. + * + * @param fields must not be {@literal null}. + * @return new instance of {@link Expire}. + */ + public static Expire expire(List fields, Duration ttl) { + + Assert.notNull(fields, "Field must not be null"); + return new Expire(null, fields, ttl); + } + + /** + * Define the {@code key} the hash is stored at. + * + * @param key must not be {@literal null}. + * @return new instance of {@link Expire}. + */ + public Expire from(ByteBuffer key) { + return new Expire(key, getFields(), ttl); + } + + /** + * @return the ttl. + */ + public Duration getTtl() { + return ttl; + } + } + + /** + * Expire a given {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. + * + * @param key must not be {@literal null}. + * @param field must not be {@literal null}. + * @param duration must not be {@literal null}. + * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + default Mono hExpire(ByteBuffer key, Duration duration, ByteBuffer field) { + Assert.notNull(duration, "Duration must not be null"); + + return hExpire(key, duration, Collections.singletonList(field)).singleOrEmpty(); + } + + /** + * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @param duration must not be {@literal null}. + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + default Flux hExpire(ByteBuffer key, Duration duration, List fields) { + Assert.notNull(duration, "Duration must not be null"); + + return hExpire(Flux.just(Expire.expire(fields, duration).from(key))) + .mapNotNull(NumericResponse::getOutput); + } + + /** + * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. + * + * @param commands must not be {@literal null}. + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; + * @since 3.5 + * @see Redis Documentation: HEXPIRE + */ + Flux> hExpire(Publisher commands); + + /** + * Expire a given {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. + * + * @param key must not be {@literal null}. + * @param field must not be {@literal null}. + * @param duration must not be {@literal null}. + * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + default Mono hpExpire(ByteBuffer key, Duration duration, ByteBuffer field) { + Assert.notNull(duration, "Duration must not be null"); + + return hpExpire(key, duration, Collections.singletonList(field)).singleOrEmpty(); + } + + /** + * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @param duration must not be {@literal null}. + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + default Flux hpExpire(ByteBuffer key, Duration duration, List fields) { + Assert.notNull(duration, "Duration must not be null"); + + return hpExpire(Flux.just(Expire.expire(fields, duration).from(key))) + .mapNotNull(NumericResponse::getOutput); + } + + /** + * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. + * + * @param commands must not be {@literal null}. + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; + * @since 3.5 + * @see Redis Documentation: HEXPIRE + */ + Flux> hpExpire(Publisher commands); + + /** + * @author Tihomir Mateev + * @see Redis Documentation: HEXPIREAT + * @since 3.5 + */ + class ExpireAt extends HashFieldsCommand { + + private final Instant expireAt; + + /** + * Creates a new {@link ExpireAt} given a {@code key}, a {@link List} of {@literal fields} and a {@link Instant} + * + * @param key can be {@literal null}. + * @param fields must not be {@literal null}. + * @param expireAt the {@link Instant} to expire at. + */ + private ExpireAt(@Nullable ByteBuffer key, List fields, Instant expireAt) { + + super(key, fields); + this.expireAt = expireAt; + } + + /** + * Specify the {@code fields} within the hash to set an expiration for. + * + * @param fields must not be {@literal null}. + * @return new instance of {@link ExpireAt}. + */ + public static ExpireAt expireAt(List fields, Instant expireAt) { + + Assert.notNull(fields, "Fields must not be null"); + return new ExpireAt(null, fields, expireAt); + } + + /** + * Define the {@code key} the hash is stored at. + * + * @param key must not be {@literal null}. + * @return new instance of {@link ExpireAt}. + */ + public ExpireAt from(ByteBuffer key) { + return new ExpireAt(key, getFields(), expireAt); + } + + /** + * @return the ttl. + */ + public Instant getExpireAt() { + return expireAt; + } + } + + /** + * Expire a given {@literal field} in a given {@link Instant} of time, indicated as an absolute + * Unix timestamp in seconds since Unix epoch + * + * @param key must not be {@literal null}. + * @param field must not be {@literal null}. + * @param expireAt must not be {@literal null}. + * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; + * @see Redis Documentation: HEXPIREAT + * @since 3.5 + */ + default Mono hExpireAt(ByteBuffer key, Instant expireAt, ByteBuffer field) { + + Assert.notNull(expireAt, "Duration must not be null"); + return hExpireAt(key, expireAt, Collections.singletonList(field)).singleOrEmpty(); + } + + /** + * Expire a {@link List} of {@literal field} in a given {@link Instant} of time, indicated as an absolute + * Unix timestamp in seconds since Unix epoch + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @param expireAt must not be {@literal null}. + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; + * @see Redis Documentation: HEXPIREAT + * @since 3.5 + */ + default Flux hExpireAt(ByteBuffer key, Instant expireAt, List fields) { + Assert.notNull(expireAt, "Duration must not be null"); + + return hExpireAt(Flux.just(ExpireAt.expireAt(fields, expireAt).from(key))).mapNotNull(NumericResponse::getOutput); + } + + /** + * Expire a {@link List} of {@literal field} in a given {@link Instant} of time, indicated as an absolute + * Unix timestamp in seconds since Unix epoch + * + * @param commands must not be {@literal null}. + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; + * @since 3.5 + * @see Redis Documentation: HEXPIREAT + */ + Flux> hExpireAt(Publisher commands); + + /** + * Expire a given {@literal field} in a given {@link Instant} of time, indicated as an absolute + * Unix timestamp in milliseconds since Unix epoch + * + * @param key must not be {@literal null}. + * @param field must not be {@literal null}. + * @param expireAt must not be {@literal null}. + * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; + * @see Redis Documentation: HPEXPIREAT + * @since 3.5 + */ + default Mono hpExpireAt(ByteBuffer key, Instant expireAt, ByteBuffer field) { + + Assert.notNull(expireAt, "Duration must not be null"); + return hpExpireAt(key, expireAt, Collections.singletonList(field)).singleOrEmpty(); + } + + /** + * Expire a {@link List} of {@literal field} in a given {@link Instant} of time, indicated as an absolute + * Unix timestamp in milliseconds since Unix epoch + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @param expireAt must not be {@literal null}. + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; + * @see Redis Documentation: HPEXPIREAT + * @since 3.5 + */ + default Flux hpExpireAt(ByteBuffer key, Instant expireAt, List fields) { + Assert.notNull(expireAt, "Duration must not be null"); + + return hpExpireAt(Flux.just(ExpireAt.expireAt(fields, expireAt).from(key))).mapNotNull(NumericResponse::getOutput); + } + + /** + * Expire a {@link List} of {@literal field} in a given {@link Instant} of time, indicated as an absolute + * Unix timestamp in milliseconds since Unix epoch + * + * @param commands must not be {@literal null}. + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; + * @since 3.5 + * @see Redis Documentation: HPEXPIREAT + */ + Flux> hpExpireAt(Publisher commands); + + /** + * Persist a given {@literal field} removing any associated expiration, measured as absolute + * Unix timestamp in seconds since Unix epoch + * + * @param key must not be {@literal null}. + * @param field must not be {@literal null}. + * @return a {@link Mono} emitting the persist result - {@code 1} indicating expiration time is removed; + * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; + * + * @see Redis Documentation: HPERSIST + * @since 3.5 + */ + default Mono hPersist(ByteBuffer key, ByteBuffer field) { + + return hPersist(key, Collections.singletonList(field)).singleOrEmpty(); + } + + /** + * Persist a given {@link List} of {@literal field} removing any associated expiration. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a {@link Flux} emitting the persisting results one by one - {@code 1} indicating expiration time is removed; + * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; + * + * @see Redis Documentation: HPERSIST + * @since 3.5 + */ + default Flux hPersist(ByteBuffer key, List fields) { + + return hPersist(Flux.just(new HashFieldsCommand(key, fields))).mapNotNull(NumericResponse::getOutput); + } + + /** + * Persist a given {@link List} of {@literal field} removing any associated expiration. + * + * @param commands must not be {@literal null}. + * @return a {@link Flux} emitting the persisting results one by one - {@code 1} indicating expiration time is removed; + * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; + * * @since 3.5 + * @see Redis Documentation: HPERSIST + */ + Flux> hPersist(Publisher commands); + + /** + * Returns the time-to-live of a given {@literal field} in seconds. + * + * @param key must not be {@literal null}. + * @param field must not be {@literal null}. + * @return a {@link Mono} emitting the TTL result - the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; + * @see Redis Documentation: HTTL + * @since 3.5 + */ + default Mono hTtl(ByteBuffer key, ByteBuffer field) { + + return hTtl(key, Collections.singletonList(field)).singleOrEmpty(); + } + + /** + * Returns the time-to-live of all the given {@literal field} in the {@link List} in seconds. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a {@link Flux} emitting the TTL results one by one - the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; + * @see Redis Documentation: HTTL + * @since 3.5 + */ + default Flux hTtl(ByteBuffer key, List fields) { + + return hTtl(Flux.just(new HashFieldsCommand(key, fields))).mapNotNull(NumericResponse::getOutput); + } + + /** + * Returns the time-to-live of all the given {@literal field} in the {@link List} in seconds. + * + * @param commands must not be {@literal null}. + * @return a {@link Flux} emitting the persisting results one by one - the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; + * @since 3.5 + * @see Redis Documentation: HTTL + */ + Flux> hTtl(Publisher commands); + + + /** + * Returns the time-to-live of a given {@literal field} in milliseconds. + * + * @param key must not be {@literal null}. + * @param field must not be {@literal null}. + * @return a {@link Mono} emitting the TTL result - the time to live in milliseconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; + * @see Redis Documentation: HPTTL + * @since 3.5 + */ + default Mono hpTtl(ByteBuffer key, ByteBuffer field) { + + return hpTtl(key, Collections.singletonList(field)).singleOrEmpty(); + } + + /** + * Returns the time-to-live of all the given {@literal field} in the {@link List} in milliseconds. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a {@link Flux} emitting the TTL results one by one - the time to live in milliseconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; + * @see Redis Documentation: HPTTL + * @since 3.5 + */ + default Flux hpTtl(ByteBuffer key, List fields) { + + return hpTtl(Flux.just(new HashFieldsCommand(key, fields))).mapNotNull(NumericResponse::getOutput); + } + + /** + * Returns the time-to-live of all the given {@literal field} in the {@link List} in milliseconds. + * + * @param commands must not be {@literal null}. + * @return a {@link Flux} emitting the persisting results one by one - the time to live in milliseconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; + * @since 3.5 + * @see Redis Documentation: HPTTL + */ + Flux> hpTtl(Publisher commands); } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java index 6385c56a57..066833d52d 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; @@ -29,6 +30,7 @@ * @author Costin Leau * @author Christoph Strobl * @author Mark Paluch + * @author Tihomir Mateev */ public interface RedisHashCommands { @@ -249,4 +251,111 @@ public interface RedisHashCommands { */ @Nullable Long hStrLen(byte[] key, byte[] field); + + /** + * Set time to live for given {@code field} in seconds. + * + * @param key must not be {@literal null}. + * @param seconds the amount of time after which the key will be expired in seconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HEXPIRE + * @since 3.4 + */ + @Nullable + List hExpire(byte[] key, long seconds, byte[]... fields); + + /** + * Set time to live for given {@code field} in milliseconds. + * + * @param key must not be {@literal null}. + * @param millis the amount of time after which the key will be expired in milliseconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HPEXPIRE + * @since 3.4 + */ + @Nullable + List hpExpire(byte[] key, long millis, byte[]... fields); + + /** + * Set the expiration for given {@code field} as a {@literal UNIX} timestamp. + * + * @param key must not be {@literal null}. + * @param unixTime the moment in time in which the field expires, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HEXPIREAT + * @since 3.4 + */ + @Nullable + List hExpireAt(byte[] key, long unixTime, byte[]... fields); + + /** + * Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds. + * + * @param key must not be {@literal null}. + * @param unixTimeInMillis the moment in time in which the field expires in milliseconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HPEXPIREAT + * @since 3.4 + */ + @Nullable + List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields); + + /** + * Remove the expiration from given {@code field}. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; + * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; + * {@literal null} when used in pipeline / transaction.{@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HPERSIST + * @since 3.4 + */ + @Nullable + List hPersist(byte[] key, byte[]... fields); + + /** + * Get the time to live for {@code field} in seconds. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.4 + */ + @Nullable + List hTtl(byte[] key, byte[]... fields); + + /** + * Get the time to live for {@code field} in and convert it to the given {@link TimeUnit}. + * + * @param key must not be {@literal null}. + * @param timeUnit must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return for each of the fields supplied - the time to live in the {@link TimeUnit} provided; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.4 + */ + @Nullable + List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields); } diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java index 2c286ce97e..f95b618cfd 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -2333,6 +2333,113 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, @Nullable Long hStrLen(String key, String field); + /** + * Set time to live for given {@code field} in seconds. + * + * @param key must not be {@literal null}. + * @param seconds the amount of time after which the key will be expired in seconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HEXPIRE + * @since 3.4 + */ + @Nullable + List hExpire(String key, long seconds, String... fields); + + /** + * Set time to live for given {@code field} in milliseconds. + * + * @param key must not be {@literal null}. + * @param millis the amount of time after which the key will be expired in milliseconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HPEXPIRE + * @since 3.4 + */ + @Nullable + List hpExpire(String key, long millis, String... fields); + + /** + * Set the expiration for given {@code field} as a {@literal UNIX} timestamp. + * + * @param key must not be {@literal null}. + * @param unixTime the moment in time in which the field expires, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HEXPIREAT + * @since 3.4 + */ + @Nullable + List hExpireAt(String key, long unixTime, String... fields); + + /** + * Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds. + * + * @param key must not be {@literal null}. + * @param unixTimeInMillis the moment in time in which the field expires in milliseconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not + * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HPEXPIREAT + * @since 3.4 + */ + @Nullable + List hpExpireAt(String key, long unixTimeInMillis, String... fields); + + /** + * Remove the expiration from given {@code field}. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; + * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; + * {@literal null} when used in pipeline / transaction.{@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HPERSIST + * @since 3.4 + */ + @Nullable + List hPersist(String key, String... fields); + + /** + * Get the time to live for {@code field} in seconds. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in milliseconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.4 + */ + @Nullable + List hTtl(String key, String... fields); + + /** + * Get the time to live for {@code field} in and convert it to the given {@link TimeUnit}. + * + * @param key must not be {@literal null}. + * @param timeUnit must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in the {@link TimeUnit} provided; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.4 + */ + @Nullable + List hTtl(String key, TimeUnit timeUnit, String... fields); + // ------------------------------------------------------------------------- // Methods dealing with HyperLogLog // ------------------------------------------------------------------------- diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java index 47ad6c6eec..c436afaeef 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisHashCommands; @@ -39,6 +40,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author John Blum + * @author Tihomir Mateev * @since 2.0 */ class JedisClusterHashCommands implements RedisHashCommands { @@ -287,6 +289,92 @@ protected ScanIteration> doScan(CursorId cursorId, ScanOpt }.open(); } + @Override + public List hExpire(byte[] key, long seconds, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(fields, "Fields must not be null"); + + try { + return connection.getCluster().hexpire(key, seconds, fields); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public List hpExpire(byte[] key, long millis, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(fields, "Fields must not be null"); + + try { + return connection.getCluster().hpexpire(key, millis, fields); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(fields, "Fields must not be null"); + + try { + return connection.getCluster().hexpireAt(key, unixTime, fields); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(fields, "Fields must not be null"); + + try { + return connection.getCluster().hpexpireAt(key, unixTimeInMillis, fields); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public List hPersist(byte[] key, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(fields, "Fields must not be null"); + + try { + return connection.getCluster().hpersist(key, fields); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public List hTtl(byte[] key, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(fields, "Fields must not be null"); + + try { + return connection.getCluster().httl(key, fields); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(fields, "Fields must not be null"); + + try { + return connection.getCluster().httl(key, fields).stream() + .map(it -> it != null ? timeUnit.convert(it, TimeUnit.SECONDS) : null) + .toList(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + @Nullable @Override public Long hStrLen(byte[] key, byte[] field) { diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java index a0ac8debf2..887412bb09 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.RedisHashCommands; @@ -43,6 +44,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author John Blum + * @author Tihomir Mateev * @since 2.0 */ class JedisHashCommands implements RedisHashCommands { @@ -250,6 +252,42 @@ protected void doClose() { }.open(); } + @Override + public List hExpire(byte[] key, long seconds, byte[]... fields) { + return connection.invoke().just(Jedis::hexpire, PipelineBinaryCommands::hexpire, key, seconds, fields); + } + + @Override + public List hpExpire(byte[] key, long millis, byte[]... fields) { + return connection.invoke().just(Jedis::hpexpire, PipelineBinaryCommands::hpexpire, key, millis, fields); + } + + @Override + public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { + return connection.invoke().just(Jedis::hexpireAt, PipelineBinaryCommands::hexpireAt, key, unixTime, fields); + } + + @Override + public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { + return connection.invoke().just(Jedis::hpexpireAt, PipelineBinaryCommands::hpexpireAt, key, unixTimeInMillis, fields); + } + + @Override + public List hPersist(byte[] key, byte[]... fields) { + return connection.invoke().just(Jedis::hpersist, PipelineBinaryCommands::hpersist, key, fields); + } + + @Override + public List hTtl(byte[] key, byte[]... fields) { + return connection.invoke().just(Jedis::httl, PipelineBinaryCommands::httl, key, fields); + } + + @Override + public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { + return connection.invoke().fromMany(Jedis::httl, PipelineBinaryCommands::httl, key, fields) + .toList(Converters.secondsToTimeUnit(timeUnit)); + } + @Nullable @Override public Long hStrLen(byte[] key, byte[] field) { diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java index e4b53f4fb4..01e683daa4 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.RedisHashCommands; @@ -39,6 +40,7 @@ /** * @author Christoph Strobl * @author Mark Paluch + * @author Tihomir Mateev * @since 2.0 */ class LettuceHashCommands implements RedisHashCommands { @@ -208,6 +210,41 @@ public Cursor> hScan(byte[] key, ScanOptions options) { return hScan(key, CursorId.initial(), options); } + @Override + public List hExpire(byte[] key, long seconds, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::hexpire, key, seconds, fields).toList(); + } + + @Override + public List hpExpire(byte[] key, long millis, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::hpexpire, key, millis, fields).toList(); + } + + @Override + public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::hexpireat, key, unixTime, fields).toList(); + } + + @Override + public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::hpexpireat, key, unixTimeInMillis, fields).toList(); + } + + @Override + public List hPersist(byte[] key, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::hpersist, key, fields).toList(); + } + + @Override + public List hTtl(byte[] key, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::httl, key, fields).toList(); + } + + @Override + public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::httl, key, fields) + .toList(Converters.secondsToTimeUnit(timeUnit)); + } /** * @param key diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java index b704321ef5..33e9c162e1 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java @@ -264,6 +264,90 @@ public Flux> hStrLen(Publisher> hExpire(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Key must not be null"); + Assert.notNull(command.getFields(), "Fields must not be null"); + + return cmd.hexpire(command.getKey(), command.getTtl().toSeconds(), command.getFields().toArray(ByteBuffer[]::new)) + .map(value -> new NumericResponse<>(command, value)); + })); + } + + @Override + public Flux> hpExpire(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Key must not be null"); + Assert.notNull(command.getFields(), "Fields must not be null"); + + return cmd.hpexpire(command.getKey(), command.getTtl().toMillis(), command.getFields().toArray(ByteBuffer[]::new)) + .map(value -> new NumericResponse<>(command, value)); + })); + } + + @Override + public Flux> hExpireAt(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Key must not be null"); + Assert.notNull(command.getFields(), "Fields must not be null"); + + return cmd.hexpireat(command.getKey(), command.getExpireAt().getEpochSecond(), command.getFields().toArray(ByteBuffer[]::new)) + .map(value -> new NumericResponse<>(command, value)); + })); + } + + @Override + public Flux> hpExpireAt(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Key must not be null"); + Assert.notNull(command.getFields(), "Fields must not be null"); + + return cmd.hpexpireat(command.getKey(), command.getExpireAt().toEpochMilli(), command.getFields().toArray(ByteBuffer[]::new)) + .map(value -> new NumericResponse<>(command, value)); + })); + } + + @Override + public Flux> hPersist(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Key must not be null"); + Assert.notNull(command.getFields(), "Fields must not be null"); + + return cmd.hpersist(command.getKey(), command.getFields().toArray(ByteBuffer[]::new)) + .map(value -> new NumericResponse<>(command, value)); + })); + } + + @Override + public Flux> hTtl(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Key must not be null"); + Assert.notNull(command.getFields(), "Fields must not be null"); + + return cmd.httl(command.getKey(), command.getFields().toArray(ByteBuffer[]::new)) + .map(value -> new NumericResponse<>(command, value)); + })); + } + + @Override + public Flux> hpTtl(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Key must not be null"); + Assert.notNull(command.getFields(), "Fields must not be null"); + + return cmd.hpttl(command.getKey(), command.getFields().toArray(ByteBuffer[]::new)) + .map(value -> new NumericResponse<>(command, value)); + })); + } + private static Map.Entry toEntry(KeyValue kv) { return new Entry() { diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java index f906462911..ff9e5b1300 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java @@ -15,10 +15,13 @@ */ package org.springframework.data.redis.core; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.lang.Nullable; @@ -29,6 +32,7 @@ * @author Christoph Strobl * @author Ninad Divadkar * @author Mark Paluch + * @author Tihomir Mateev */ public interface BoundHashOperations extends BoundKeyOperations { @@ -153,6 +157,78 @@ public interface BoundHashOperations extends BoundKeyOperations { @Nullable Long lengthOfValue(HK hashKey); + /** + * Set time to live for given {@code hashKey} (aka field). + * + * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); {@code -2} + * indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @throws IllegalArgumentException if the timeout is {@literal null}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable + List expire(Duration timeout, Collection hashKeys); + + /** + * Set the expiration for given {@code hashKey} (aka field) as a {@literal date} timestamp. + * + * @param expireAt must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable + List expireAt(Instant expireAt, Collection hashKeys); + + /** + * Remove the expiration from given {@code hashKey} (aka field). + * + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; + * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; {@literal null} when + * used in pipeline / transaction. + * @see Redis Documentation: HPERSIST + * @since 3.5 + */ + @Nullable + List persist(Collection hashKeys); + + /** + * Get the time to live for {@code hashKey} (aka field) in seconds. + * + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command + * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + List getExpire(Collection hashKeys); + + /** + * Get the time to live for {@code hashKey} (aka field) and convert it to the given {@link TimeUnit}. + * + * @param timeUnit must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command + * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + List getExpire(TimeUnit timeUnit, Collection hashKeys); + /** * Get size of hash at the bound key. * diff --git a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java index 974e20e13f..5df4422e48 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java @@ -15,6 +15,8 @@ */ package org.springframework.data.redis.core; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; @@ -22,6 +24,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.core.convert.converter.Converter; import org.springframework.data.redis.connection.convert.Converters; @@ -34,6 +37,7 @@ * @author Costin Leau * @author Christoph Strobl * @author Ninad Divadkar + * @author Tihomir Mateev */ class DefaultHashOperations extends AbstractOperations implements HashOperations { @@ -210,6 +214,47 @@ public Boolean putIfAbsent(K key, HK hashKey, HV value) { return execute(connection -> connection.hSetNX(rawKey, rawHashKey, rawHashValue)); } + @Override + public List expire(K key, Duration duration, Collection hashKeys) { + byte[] rawKey = rawKey(key); + byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); + long rawTimeout = duration.toMillis(); + + return execute(connection -> connection.hpExpire(rawKey, rawTimeout, rawHashKeys)); + } + + @Override + public List expireAt(K key, Instant instant, Collection hashKeys) { + byte[] rawKey = rawKey(key); + byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); + + return execute(connection -> connection.hpExpireAt(rawKey, instant.toEpochMilli(), rawHashKeys)); + } + + @Override + public List persist(K key, Collection hashKeys) { + byte[] rawKey = rawKey(key); + byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); + + return execute(connection -> connection.hPersist(rawKey, rawHashKeys)); + } + + @Override + public List getExpire(K key, Collection hashKeys) { + byte[] rawKey = rawKey(key); + byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); + + return execute(connection -> connection.hTtl(rawKey, rawHashKeys)); + } + + @Override + public List getExpire(K key, TimeUnit timeUnit, Collection hashKeys) { + byte[] rawKey = rawKey(key); + byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); + + return execute(connection -> connection.hTtl(rawKey, timeUnit, rawHashKeys)); + } + @Override public List values(K key) { diff --git a/src/main/java/org/springframework/data/redis/core/HashOperations.java b/src/main/java/org/springframework/data/redis/core/HashOperations.java index 8a2c6641ad..ea17d26e2d 100644 --- a/src/main/java/org/springframework/data/redis/core/HashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/HashOperations.java @@ -15,10 +15,13 @@ */ package org.springframework.data.redis.core; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.lang.Nullable; @@ -28,6 +31,7 @@ * @author Costin Leau * @author Christoph Strobl * @author Ninad Divadkar + * @author Tihomir Mateev */ public interface HashOperations { @@ -221,6 +225,82 @@ public interface HashOperations { */ Cursor> scan(H key, ScanOptions options); + /** + * Set time to live for given {@code hashKey} (aka field). + * + * @param key must not be {@literal null}. + * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); {@code -2} + * indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @throws IllegalArgumentException if the timeout is {@literal null}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable + List expire(H key, Duration timeout, Collection hashKeys); + + /** + * Set the expiration for given {@code hashKey} (aka field) as a {@literal date} timestamp. + * + * @param key must not be {@literal null}. + * @param expireAt must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable + List expireAt(H key, Instant expireAt, Collection hashKeys); + + /** + * Remove the expiration from given {@code hashKey} (aka field). + * + * @param key must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; + * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; {@literal null} when + * used in pipeline / transaction. + * @see Redis Documentation: HPERSIST + * @since 3.5 + */ + @Nullable + List persist(H key, Collection hashKeys); + + /** + * Get the time to live for {@code hashKey} (aka field) in seconds. + * + * @param key must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command + * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + List getExpire(H key, Collection hashKeys); + + /** + * Get the time to live for {@code hashKey} (aka field) and convert it to the given {@link TimeUnit}. + * + * @param key must not be {@literal null}. + * @param timeUnit must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command + * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + List getExpire(H key, TimeUnit timeUnit, Collection hashKeys); /** * @return never {@literal null}. */ diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java index 9dd0274783..547c351875 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java @@ -15,10 +15,14 @@ */ package org.springframework.data.redis.support.collections; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -37,6 +41,7 @@ * @author Costin Leau * @author Christoph Strobl * @author Christian Bühler + * @author Tihomir Mateev */ public class DefaultRedisMap implements RedisMap { @@ -321,6 +326,31 @@ public Cursor> scan() { return scan(ScanOptions.NONE); } + @Override + public List expire(Duration timeout, Collection hashKeys) { + return Objects.requireNonNull(hashOps.expire(timeout, hashKeys)); + } + + @Override + public List expireAt(Instant expireAt, Collection hashKeys) { + return Objects.requireNonNull(hashOps.expireAt(expireAt, hashKeys)); + } + + @Override + public List persist(Collection hashKeys) { + return Objects.requireNonNull(hashOps.persist(hashKeys)); + } + + @Override + public List getExpire(Collection hashKeys) { + return Objects.requireNonNull(hashOps.getExpire(hashKeys)); + } + + @Override + public List getExpire(TimeUnit timeUnit, Collection hashKeys) { + return Objects.requireNonNull(hashOps.getExpire(timeUnit, hashKeys)); + } + private void checkResult(@Nullable Object obj) { if (obj == null) { throw new IllegalStateException("Cannot read collection with Redis connection in pipeline/multi-exec mode"); diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java index a2eb3b8985..4b79cf0290 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java @@ -15,9 +15,14 @@ */ package org.springframework.data.redis.support.collections; +import java.time.Duration; +import java.time.Instant; +import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; import org.springframework.lang.Nullable; @@ -26,6 +31,7 @@ * * @author Costin Leau * @author Christoph Strobl + * @author Tihomi Mateev */ public interface RedisMap extends RedisStore, ConcurrentMap { @@ -71,4 +77,71 @@ public interface RedisMap extends RedisStore, ConcurrentMap { * @return */ Iterator> scan(); + + /** + * Set time to live for given {hash {@code key}. + * + * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); {@code -2} + * indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @throws IllegalArgumentException if the timeout is {@literal null}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + List expire(Duration timeout, Collection hashKeys); + + /** + * Set the expiration for given hash {@code key} as a {@literal date} timestamp. + * + * @param expireAt must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + List expireAt(Instant expireAt, Collection hashKeys); + + /** + * Remove the expiration from given hash {@code key}. + * + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; + * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; {@literal null} when + * used in pipeline / transaction. + * @see Redis Documentation: HPERSIST + * @since 3.5 + */ + List persist(Collection hashKeys); + + /** + * Get the time to live for hash {@code key} in seconds. + * + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command + * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + List getExpire(Collection hashKeys); + + /** + * Get the time to live for hash {@code key} and convert it to the given {@link TimeUnit}. + * + * @param timeUnit must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command + * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + List getExpire(TimeUnit timeUnit, Collection hashKeys); } diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java index d02d86b6fc..68981643d9 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java @@ -17,16 +17,10 @@ import java.io.IOException; import java.io.OutputStream; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Map; +import java.time.Duration; +import java.time.Instant; +import java.util.*; import java.util.Map.Entry; -import java.util.Properties; -import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.connection.DataType; @@ -304,4 +298,35 @@ public synchronized void storeToXML(OutputStream os, String comment) throws IOEx public Iterator> scan() { throw new UnsupportedOperationException(); } + + @Override + public List expire(Duration timeout, Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); + return Objects.requireNonNull(hashOps.expire(timeout, keys)); + } + + @Override + public List expireAt(Instant expireAt, Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); + return Objects.requireNonNull(hashOps.expireAt(expireAt, keys)); + } + + @Override + public List persist(Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); + return Objects.requireNonNull(hashOps.persist(keys)); + } + + @Override + public List getExpire(Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); + return Objects.requireNonNull(hashOps.getExpire(keys)); + } + + @Override + public List getExpire(TimeUnit timeUnit, Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); + return Objects.requireNonNull(hashOps.getExpire(timeUnit, keys)); + } + } diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index 4662437afe..d42153cb68 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -113,6 +113,7 @@ * @author Hendrik Duerkop * @author Shyngys Sapraliyev * @author Roman Osadchuk + * @author Tihomir Mateev */ public abstract class AbstractConnectionIntegrationTests { @@ -3432,6 +3433,194 @@ void hStrLenReturnsZeroWhenKeyDoesNotExist() { verifyResults(Arrays.asList(new Object[] { 0L })); } + @Test + @EnabledOnCommand("HEXPIRE") + public void hExpireReturnsSuccessAndSetsTTL() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hExpire("hash-hexpire", 5L, "key-2")); + actual.add(connection.hTtl("hash-hexpire", "key-2")); + + List results = getResults(); + assertThat(results.get(0)).isEqualTo(Boolean.TRUE); + assertThat((List) results.get(1)).contains(1L); + assertThat((List) results.get(2)).allSatisfy( value -> assertThat((Long)value).isBetween(0L, 5L)); + } + + @Test + @EnabledOnCommand("HEXPIRE") + public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hExpire("hash-hexpire", 5L, "missking-field")); + actual.add(connection.hExpire("missing-key", 5L, "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); + } + + @Test + @EnabledOnCommand("HEXPIRE") + public void hExpireReturnsTwoWhenZeroProvided() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hExpire("hash-hexpire", 0, "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(2L))); + } + + @Test + @EnabledOnCommand("HPEXPIRE") + public void hpExpireReturnsSuccessAndSetsTTL() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hpExpire("hash-hexpire", 5000L, "key-2")); + actual.add(connection.hTtl("hash-hexpire", TimeUnit.MILLISECONDS,"key-2")); + + List results = getResults(); + assertThat(results.get(0)).isEqualTo(Boolean.TRUE); + assertThat((List) results.get(1)).contains(1L); + assertThat((List) results.get(2)).allSatisfy( value -> assertThat((Long)value).isBetween(0L, 5000L)); + } + + @Test + @EnabledOnCommand("HPEXPIRE") + public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hpExpire("hash-hexpire", 5L, "missing-field")); + actual.add(connection.hpExpire("missing-key", 5L, "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); + } + + @Test + @EnabledOnCommand("HPEXPIRE") + public void hpExpireReturnsTwoWhenZeroProvided() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hpExpire("hash-hexpire", 0, "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(2L))); + } + + @Test + @EnabledOnCommand("HEXPIREAT") + public void hExpireAtReturnsSuccessAndSetsTTL() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); + + actual.add(connection.hExpireAt("hash-hexpire", inFiveSeconds, "key-2")); + actual.add(connection.hTtl("hash-hexpire", "key-2")); + + List results = getResults(); + assertThat(results.get(0)).isEqualTo(Boolean.TRUE); + assertThat((List) results.get(1)).contains(1L); + assertThat((List) results.get(2)).allSatisfy( value -> assertThat((Long)value).isBetween(0L, 5L)); + } + + @Test + @EnabledOnCommand("HEXPIREAT") + public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); + + actual.add(connection.hpExpire("hash-hexpire", inFiveSeconds, "missing-field")); + actual.add(connection.hpExpire("missing-key", inFiveSeconds, "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); + } + + @Test + @EnabledOnCommand("HEXPIREAT") + public void hExpireAtReturnsTwoWhenZeroProvided() { + long fiveSecondsAgo = Instant.now().minusSeconds(5L).getEpochSecond(); + + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hExpireAt("hash-hexpire", fiveSecondsAgo, "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(2L))); + } + + @Test + @EnabledOnCommand("HEXPIREAT") + public void hpExpireAtReturnsSuccessAndSetsTTL() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); + + actual.add(connection.hpExpireAt("hash-hexpire", inFiveSeconds, "key-2")); + actual.add(connection.hTtl("hash-hexpire", "key-2")); + + List results = getResults(); + assertThat(results.get(0)).isEqualTo(Boolean.TRUE); + assertThat((List) results.get(1)).contains(1L); + assertThat((List) results.get(2)).allSatisfy( value -> assertThat((Long)value).isBetween(0L, 5L)); + } + + @Test + @EnabledOnCommand("HEXPIREAT") + public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); + + actual.add(connection.hpExpireAt("hash-hexpire", inFiveSeconds, "missing-field")); + actual.add(connection.hpExpireAt("missing-key", inFiveSeconds, "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); + } + + @Test + @EnabledOnCommand("HPEXPIREAT") + public void hpExpireAdReturnsTwoWhenZeroProvided() { + long fiveSecondsAgo = Instant.now().minusSeconds(5L).getEpochSecond(); + + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hpExpireAt("hash-hexpire", fiveSecondsAgo, "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(2L))); + } + + @Test + @EnabledOnCommand("HPERSIST") + public void hPersistReturnsSuccessAndPersistsField() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hExpire("hash-hexpire", 5L, "key-2")); + actual.add(connection.hPersist("hash-hexpire", "key-2")); + actual.add(connection.hTtl("hash-hexpire", "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(1L), List.of(1L), List.of(-1L))); + } + + @Test + @EnabledOnCommand("HPERSIST") + public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hPersist("hash-hexpire", "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(-1L))); + } + + @Test + @EnabledOnCommand("HPERSIST") + public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hPersist("hash-hexpire", "missing-field")); + actual.add(connection.hPersist("missing-key", "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); + } + + @Test + @EnabledOnCommand("HTTL") + public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hTtl("hash-hexpire", "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(-1L))); + } + + @Test + @EnabledOnCommand("HTTL") + public void hTtlReturnsMinusTwoWhenFieldOrKeyMissing() { + actual.add(connection.hTtl("hash-hexpire", "missing-field")); + actual.add(connection.hTtl("missing-key", "key-2")); + + verifyResults(Arrays.asList(new Object[] { List.of(-2L), List.of(-2L) })); + } + @Test // DATAREDIS-694 void touchReturnsNrOfKeysTouched() { diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 6fe6eb43c1..022d2e6a52 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -36,6 +36,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.time.Instant; import java.util.*; import java.util.concurrent.TimeUnit; @@ -84,6 +85,7 @@ * @author Mark Paluch * @author Pavel Khokhlov * @author Dennis Neufeld + * @author Tihomir Mateev */ @EnabledOnRedisClusterAvailable @ExtendWith(JedisExtension.class) @@ -1038,6 +1040,148 @@ public void hStrLenReturnsZeroWhenKeyDoesNotExist() { assertThat(clusterConnection.hashCommands().hStrLen(KEY_1_BYTES, KEY_1_BYTES)).isEqualTo(0L); } + @Test + public void hExpireReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)) + .allSatisfy(val -> assertThat(val).isBetween(0L, 5L)); + } + + @Test + public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + // missing field + assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_1_BYTES)).contains(-2L); + // missing key + assertThat(clusterConnection.hashCommands().hExpire(KEY_2_BYTES, 5L, KEY_2_BYTES)).contains(-2L); + } + + @Test + public void hExpireReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); + } + + @Test + public void hpExpireReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 5000L, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS,KEY_2_BYTES)) + .allSatisfy(val -> assertThat(val).isBetween(0L, 5000L)); + } + + @Test + public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + // missing field + assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 5L, KEY_1_BYTES)).contains(-2L); + // missing key + assertThat(clusterConnection.hashCommands().hpExpire(KEY_2_BYTES, 5L, KEY_2_BYTES)).contains(-2L); + } + + @Test + public void hpExpireReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); + } + + @Test + public void hExpireAtReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)).allSatisfy(val -> assertThat(val).isBetween(0L, 5L)); + } + + @Test + public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); + + // missing field + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_1_BYTES)).contains(-2L); + // missing key + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_2_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(-2L); + } + + @Test + public void hExpireAdReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); + } + + @Test + public void hpExpireAtReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS, KEY_2_BYTES)) + .allSatisfy(val -> assertThat(val).isBetween(0L, 5000L)); + } + + @Test + public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); + + // missing field + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_1_BYTES)).contains(-2L); + // missing key + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_2_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(-2L); + } + + @Test + public void hpExpireAdReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); + } + + @Test + public void hPersistReturnsSuccessAndPersistsField() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); + } + + @Test + public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); + } + + @Test + public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); + assertThat(clusterConnection.hashCommands().hPersist(KEY_3_BYTES,KEY_2_BYTES)).contains(-2L); + + } + + @Test + public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); + + } + + @Test + public void hTtlReturnsMinusTwoWhenFieldOrKeyMissing() { + + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); + assertThat(clusterConnection.hashCommands().hTtl(KEY_3_BYTES,KEY_2_BYTES)).contains(-2L); + + } + @Test // DATAREDIS-315 public void hValsShouldRetrieveValuesCorrectly() { diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java index b1fe07dae6..c779322576 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java @@ -32,6 +32,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.time.Instant; import java.util.*; import java.util.concurrent.TimeUnit; @@ -73,6 +74,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author Dennis Neufeld + * @author Tihomir Mateev */ @SuppressWarnings("deprecation") @EnabledOnRedisClusterAvailable @@ -1095,6 +1097,147 @@ public void hStrLenReturnsZeroWhenKeyDoesNotExist() { assertThat(clusterConnection.hashCommands().hStrLen(KEY_1_BYTES, KEY_1_BYTES)).isEqualTo(0L); } + @Test + public void hExpireReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hTtl(KEY_1_BYTES, KEY_2_BYTES)).allSatisfy(val -> assertThat(val).isBetween(0L, 5L)); + } + + @Test + public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + // missing field + assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_1_BYTES)).contains(-2L); + // missing key + assertThat(clusterConnection.hashCommands().hExpire(KEY_2_BYTES, 5L, KEY_2_BYTES)).contains(-2L); + } + + @Test + public void hExpireReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); + } + + @Test + public void hpExpireReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 5000L, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS,KEY_2_BYTES)) + .allSatisfy(val -> assertThat(val).isBetween(0L, 5000L)); + } + + @Test + public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + // missing field + assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 5L, KEY_1_BYTES)).contains(-2L); + // missing key + assertThat(clusterConnection.hashCommands().hpExpire(KEY_2_BYTES, 5L, KEY_2_BYTES)).contains(-2L); + } + + @Test + public void hpExpireReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); + } + + @Test + public void hExpireAtReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hTtl(KEY_1_BYTES, KEY_2_BYTES)).allSatisfy(val -> assertThat(val).isBetween(0L, 5L)); + } + + @Test + public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); + + // missing field + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_1_BYTES)).contains(-2L); + // missing key + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_2_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(-2L); + } + + @Test + public void hExpireAdReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); + } + + @Test + public void hpExpireAtReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS, KEY_2_BYTES)) + .allSatisfy(val -> assertThat(val).isBetween(0L, 5000L)); + } + + @Test + public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); + + // missing field + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_1_BYTES)).contains(-2L); + // missing key + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_2_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(-2L); + } + + @Test + public void hpExpireAdReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); + } + + @Test + public void hPersistReturnsSuccessAndPersistsField() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hTtl(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); + } + + @Test + public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); + } + + @Test + public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); + assertThat(clusterConnection.hashCommands().hPersist(KEY_3_BYTES,KEY_2_BYTES)).contains(-2L); + + } + + @Test + public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); + + } + + @Test + public void hTtlReturnsMinusTwoWhenFieldOrKeyMissing() { + + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); + assertThat(clusterConnection.hashCommands().hTtl(KEY_3_BYTES,KEY_2_BYTES)).contains(-2L); + + } + @Test // DATAREDIS-315 public void hValsShouldRetrieveValuesCorrectly() { diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java index 973e061f90..86b3ca74f1 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java @@ -21,6 +21,8 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -35,6 +37,7 @@ * * @author Christoph Strobl * @author Mark Paluch + * @author Tihomir Mateev */ public class LettuceReactiveHashCommandsIntegrationTests extends LettuceReactiveCommandsTestSupport { @@ -288,4 +291,61 @@ void hStrLenReturnsZeroWhenKeyDoesNotExist() { connection.hashCommands().hStrLen(KEY_1_BBUFFER, FIELD_1_BBUFFER).as(StepVerifier::create).expectNext(0L) // .verifyComplete(); } + + @ParameterizedRedisTest + void hExpireShouldHandleMultipleParametersCorrectly() { + assertThat(nativeCommands.hset(KEY_1, FIELD_1, VALUE_1)).isTrue(); + assertThat(nativeCommands.hset(KEY_1, FIELD_2, VALUE_2)).isTrue(); + final var fields = Arrays.asList(FIELD_1_BBUFFER, FIELD_2_BBUFFER, FIELD_3_BBUFFER); + + connection.hashCommands().hExpire(KEY_1_BBUFFER, Duration.ofSeconds(1), fields).as(StepVerifier::create) // + .expectNext(1L) + .expectNext(1L) + .expectNext(-2L) + .expectComplete() + .verify(); + + assertThat(nativeCommands.httl(KEY_1, FIELD_1)).allSatisfy(it -> assertThat(it).isBetween(0L, 1000L)); + assertThat(nativeCommands.httl(KEY_1, FIELD_2)).allSatisfy(it -> assertThat(it).isBetween(0L, 1000L)); + assertThat(nativeCommands.httl(KEY_1, FIELD_3)).allSatisfy(it -> assertThat(it).isEqualTo(-2L)); + + } + + @ParameterizedRedisTest + void hExpireAtShouldHandleMultipleParametersCorrectly() { + assertThat(nativeCommands.hset(KEY_1, FIELD_1, VALUE_1)).isTrue(); + assertThat(nativeCommands.hset(KEY_1, FIELD_2, VALUE_2)).isTrue(); + final var fields = Arrays.asList(FIELD_1_BBUFFER, FIELD_2_BBUFFER, FIELD_3_BBUFFER); + + connection.hashCommands().hExpireAt(KEY_1_BBUFFER, Instant.now().plusSeconds(1), fields).as(StepVerifier::create) // + .expectNext(1L) + .expectNext(1L) + .expectNext(-2L) + .expectComplete() + .verify(); + + assertThat(nativeCommands.httl(KEY_1, FIELD_1, FIELD_2)).allSatisfy(it -> assertThat(it).isBetween(0L, 1000L)); + assertThat(nativeCommands.httl(KEY_1, FIELD_3)).allSatisfy(it -> assertThat(it).isEqualTo(-2L)); + + } + + @ParameterizedRedisTest + void hPersistShouldPersistFields() { + assertThat(nativeCommands.hset(KEY_1, FIELD_1, VALUE_1)).isTrue(); + assertThat(nativeCommands.hset(KEY_1, FIELD_2, VALUE_2)).isTrue(); + + assertThat(nativeCommands.hexpire(KEY_1, 1000, FIELD_1)) + .allSatisfy(it -> assertThat(it).isEqualTo(1L)); + + final var fields = Arrays.asList(FIELD_1_BBUFFER, FIELD_2_BBUFFER, FIELD_3_BBUFFER); + + connection.hashCommands().hPersist(KEY_1_BBUFFER, fields).as(StepVerifier::create) // + .expectNext(1L) + .expectNext(-1L) + .expectNext(-2L) + .expectComplete() + .verify(); + + assertThat(nativeCommands.httl(KEY_1, FIELD_1, FIELD_2)).allSatisfy(it -> assertThat(it).isEqualTo(-1L)); + } } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java index 8a2b7065ad..ef9737d7a4 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java @@ -19,9 +19,13 @@ import static org.assertj.core.api.Assumptions.*; import java.io.IOException; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; @@ -39,6 +43,7 @@ * * @author Jennifer Hickey * @author Christoph Strobl + * @author Tihomir Mateev * @param Key type * @param Hash key type * @param Hash value type @@ -202,4 +207,79 @@ void randomValue() { Map values = hashOps.randomEntries(key, 10); assertThat(values).hasSize(2).containsEntry(key1, val1).containsEntry(key2, val2); } + + @ParameterizedRedisTest + void testExpireAndGetExpireMillis() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + hashOps.put(key, key1, val1); + hashOps.put(key, key2, val2); + + assertThat(redisTemplate.opsForHash().expire(key, Duration.ofMillis(500), List.of(key1))) + .containsExactly(1L); + + assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1))) + .allSatisfy(it -> assertThat(it).isBetween(0L, 500L)); + } + + @ParameterizedRedisTest + void testExpireAndGetExpireSeconds() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + hashOps.put(key, key1, val1); + hashOps.put(key, key2, val2); + + assertThat(redisTemplate.opsForHash().expire(key, Duration.ofSeconds(5), List.of(key1, key2))) + .containsExactly(1L, 1L); + + assertThat(redisTemplate.opsForHash().getExpire(key, TimeUnit.SECONDS, List.of(key1, key2))) + .allSatisfy(it -> assertThat(it).isBetween(0L, 5L)); + } + + @ParameterizedRedisTest + void testExpireAtAndGetExpireMillis() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + hashOps.put(key, key1, val1); + hashOps.put(key, key2, val2); + + assertThat(redisTemplate.opsForHash().expireAt(key, Instant.now().plusMillis(500), List.of(key1, key2))) + .containsExactly(1L, 1L); + + assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1, key2))) + .allSatisfy(it -> assertThat(it).isBetween(0L, 500L)); + } + + @ParameterizedRedisTest + void testPersistAndGetExpireMillis() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + hashOps.put(key, key1, val1); + hashOps.put(key, key2, val2); + + assertThat(redisTemplate.opsForHash().expireAt(key, Instant.now().plusMillis(500), List.of(key1, key2))) + .containsExactly(1L, 1L); + + assertThat(redisTemplate.opsForHash().persist(key, List.of(key1, key2))) + .allSatisfy(it -> assertThat(it).isEqualTo(1L)); + + assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1, key2))) + .allSatisfy(it -> assertThat(it).isEqualTo(-1L)); + } } diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java index 435d5b4500..0f364c45c6 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java @@ -20,14 +20,18 @@ import java.io.IOException; import java.text.DecimalFormat; +import java.time.Duration; +import java.time.Instant; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assumptions; import org.junit.jupiter.api.BeforeEach; @@ -190,6 +194,34 @@ void testIncrement() { assertThat(map.increment(k1, 10)).isEqualTo(Long.valueOf(Long.valueOf((String) v1) + 10)); } + @ParameterizedRedisTest + void testExpire() { + K k1 = getKey(); + V v1 = getValue(); + assertThat(map.put(k1, v1)).isEqualTo(null); + + Collection keys = Collections.singletonList(k1); + assertThat(map.expire(Duration.ofSeconds(5), keys)).contains(1L); + assertThat(map.getExpire(keys)).allSatisfy(expiration -> assertThat(expiration).isBetween(1L, 5L)); + assertThat(map.getExpire(TimeUnit.MILLISECONDS, keys)) + .allSatisfy(expiration -> assertThat(expiration).isBetween(1000L, 5000L)); + assertThat(map.persist(keys)).contains(1L); + } + + @ParameterizedRedisTest + void testExpireAt() { + K k1 = getKey(); + V v1 = getValue(); + assertThat(map.put(k1, v1)).isEqualTo(null); + + Collection keys = Collections.singletonList(k1); + assertThat(map.expireAt(Instant.now().plusSeconds(5), keys)).contains(1L); + assertThat(map.getExpire(keys)).allSatisfy(expiration -> assertThat(expiration).isBetween(1L, 5L)); + assertThat(map.getExpire(TimeUnit.MILLISECONDS, keys)) + .allSatisfy(expiration -> assertThat(expiration).isBetween(1000L, 5000L)); + assertThat(map.persist(keys)).contains(1L); + } + @ParameterizedRedisTest void testIncrementDouble() { assumeThat(valueFactory instanceof DoubleAsStringObjectFactory).isTrue(); @@ -496,4 +528,5 @@ public void randomEntryFromHash() { assertThat(map.randomEntry()).isIn(new AbstractMap.SimpleImmutableEntry(k1, v1), new AbstractMap.SimpleImmutableEntry(k2, v2)); } + } From 5228bc700895c38d4286d5cc1dcc7901c97be822 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 3 Feb 2025 11:05:47 +0100 Subject: [PATCH 138/187] Add field expiration options to reactive API. Make sure tests do not run when targeting older server versions. Introduce dedicated objects for time to live and status changes. See #3054 --- .../DefaultStringRedisConnection.java | 21 ++ .../connection/DefaultedRedisConnection.java | 16 + .../data/redis/connection/Hash.java | 115 +++++++ .../connection/ReactiveHashCommands.java | 321 +++++++----------- .../redis/connection/RedisHashCommands.java | 95 +++++- .../redis/connection/RedisKeyCommands.java | 59 ++++ .../connection/StringRedisConnection.java | 23 +- .../jedis/JedisClusterHashCommands.java | 61 +++- .../connection/jedis/JedisHashCommands.java | 58 +++- .../lettuce/LettuceHashCommands.java | 49 +++ .../lettuce/LettuceReactiveHashCommands.java | 65 ++-- .../data/redis/core/BoundHashOperations.java | 18 +- .../redis/core/DefaultHashOperations.java | 79 ++++- .../core/DefaultReactiveHashOperations.java | 98 +++++- .../data/redis/core/Expirations.java | 303 +++++++++++++++++ .../data/redis/core/ExpireChanges.java | 195 +++++++++++ .../data/redis/core/HashOperations.java | 17 +- .../redis/core/ReactiveHashOperations.java | 77 +++++ .../data/redis/core/TimeoutUtils.java | 6 +- .../data/redis/core/types/Expiration.java | 5 +- .../support/collections/DefaultRedisMap.java | 13 +- .../redis/support/collections/RedisMap.java | 13 +- .../support/collections/RedisProperties.java | 27 +- .../AbstractConnectionIntegrationTests.java | 29 +- .../jedis/JedisClusterConnectionTests.java | 18 +- .../LettuceClusterConnectionTests.java | 39 ++- ...eReactiveHashCommandsIntegrationTests.java | 4 + ...DefaultHashOperationsIntegrationTests.java | 119 ++++++- ...eactiveHashOperationsIntegrationTests.java | 151 +++++++- .../data/redis/core/ExpirationsUnitTest.java | 94 +++++ .../AbstractRedisMapIntegrationTests.java | 36 +- 31 files changed, 1859 insertions(+), 365 deletions(-) create mode 100644 src/main/java/org/springframework/data/redis/connection/Hash.java create mode 100644 src/main/java/org/springframework/data/redis/core/Expirations.java create mode 100644 src/main/java/org/springframework/data/redis/core/ExpireChanges.java create mode 100644 src/test/java/org/springframework/data/redis/core/ExpirationsUnitTest.java diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java index dd03f14bc5..c3f13ef4a5 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -30,6 +30,7 @@ import org.springframework.data.geo.Metric; import org.springframework.data.geo.Point; import org.springframework.data.redis.RedisSystemException; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.connection.convert.ListConverter; import org.springframework.data.redis.connection.convert.MapConverter; @@ -2571,6 +2572,11 @@ public Long hStrLen(byte[] key, byte[] field) { return convertAndReturn(delegate.hStrLen(key, field), Converters.identityConverter()); } + public @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, + FieldExpirationOptions options, byte[]... fields) { + return this.delegate.expireHashField(key, expiration, options, fields); + } + @Override public List hExpire(byte[] key, long seconds, byte[]... fields) { return this.delegate.hExpire(key, seconds, fields); @@ -2601,11 +2607,21 @@ public List hTtl(byte[] key, byte[]... fields) { return this.delegate.hTtl(key, fields); } + @Override + public List hpTtl(byte[] key, byte[]... fields) { + return this.delegate.hpTtl(key, fields); + } + @Override public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { return this.delegate.hTtl(key, timeUnit, fields); } + public @Nullable List expireHashField(String key, org.springframework.data.redis.core.types.Expiration expiration, + FieldExpirationOptions options, String... fields) { + return expireHashField(serialize(key), expiration, options, serializeMulti(fields)); + } + @Override public List hExpire(String key, long seconds, String... fields) { return hExpire(serialize(key), seconds, serializeMulti(fields)); @@ -2641,6 +2657,11 @@ public List hTtl(String key, TimeUnit timeUnit, String... fields) { return hTtl(serialize(key), timeUnit, serializeMulti(fields)); } + @Override + public List hpTtl(String key, String... fields) { + return hTtl(serialize(key), serializeMulti(fields)); + } + @Override public void setClientName(byte[] name) { this.delegate.setClientName(name); diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java index 3a83343adb..979cf53009 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -28,6 +28,7 @@ import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.Metric; import org.springframework.data.geo.Point; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.stream.ByteRecord; import org.springframework.data.redis.connection.stream.Consumer; import org.springframework.data.redis.connection.stream.MapRecord; @@ -1527,6 +1528,21 @@ default List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { return hashCommands().hTtl(key, timeUnit, fields); } + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hpTtl(byte[] key, byte[]... fields) { + return hashCommands().hpTtl(key, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, + FieldExpirationOptions options, byte[]... fields) { + return hashCommands().expireHashField(key, expiration, options, fields); + } + // GEO COMMANDS /** @deprecated in favor of {@link RedisConnection#geoCommands()}}. */ diff --git a/src/main/java/org/springframework/data/redis/connection/Hash.java b/src/main/java/org/springframework/data/redis/connection/Hash.java new file mode 100644 index 0000000000..c8d0e8a248 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/connection/Hash.java @@ -0,0 +1,115 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.connection; + +import java.util.Objects; + +import org.springframework.lang.Contract; +import org.springframework.lang.Nullable; +import org.springframework.util.ObjectUtils; + +/** + * @author Christoph Strobl + * @since 3.5 + */ +public interface Hash { + + class FieldExpirationOptions { + + private static final FieldExpirationOptions NONE = new FieldExpirationOptions(null); + private @Nullable Condition condition; + + FieldExpirationOptions(@Nullable Condition condition) { + this.condition = condition; + } + + public static FieldExpirationOptions none() { + return NONE; + } + + @Contract("_ -> new") + public static FieldExpireOptionsBuilder builder() { + return new FieldExpireOptionsBuilder(); + } + + public @Nullable Condition getCondition() { + return condition; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FieldExpirationOptions that = (FieldExpirationOptions) o; + return ObjectUtils.nullSafeEquals(this.condition, that.condition); + } + + @Override + public int hashCode() { + return Objects.hash(condition); + } + + public static class FieldExpireOptionsBuilder { + + @Nullable Condition condition; + + @Contract("_ -> this") + public FieldExpireOptionsBuilder nx() { + this.condition = Condition.NX; + return this; + } + + @Contract("_ -> this") + public FieldExpireOptionsBuilder xx() { + this.condition = Condition.XX; + return this; + } + + @Contract("_ -> this") + public FieldExpireOptionsBuilder gt() { + this.condition = Condition.GT; + return this; + } + + @Contract("_ -> this") + public FieldExpireOptionsBuilder lt() { + this.condition = Condition.LT; + return this; + } + + @Contract("_ -> !null") + public FieldExpirationOptions build() { + return condition == null ? NONE : new FieldExpirationOptions(condition); + } + } + + public enum Condition { + + /** Set expiration only when the field has no expiration. */ + NX, + /** Set expiration only when the field has an existing expiration. */ + XX, + /** Set expiration only when the new expiration is greater than current one. */ + GT, + /** Set expiration only when the new expiration is greater than current one. */ + LT + } + } +} diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java index 35e3437141..f58f4e32a9 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java @@ -26,10 +26,12 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import org.reactivestreams.Publisher; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.Command; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; @@ -38,6 +40,7 @@ import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -846,54 +849,59 @@ default Mono hStrLen(ByteBuffer key, ByteBuffer field) { Flux> hStrLen(Publisher commands); /** - * @author Tihomir Mateev - * @see Redis Documentation: HEXPIRE * @since 3.5 */ - class Expire extends HashFieldsCommand { + class ExpireCommand extends HashFieldsCommand { - private final Duration ttl; - - /** - * Creates a new {@link Expire} given a {@code key}, a {@link List} of {@code fields} and a time-to-live - * - * @param key can be {@literal null}. - * @param fields must not be {@literal null}. - * @param ttl the duration of the time to live. - */ - private Expire(@Nullable ByteBuffer key, List fields, Duration ttl) { + private final Expiration expiration; + private final FieldExpirationOptions options; + private ExpireCommand(@Nullable ByteBuffer key, List fields, Expiration expiration, + FieldExpirationOptions options) { super(key, fields); - this.ttl = ttl; + this.expiration = expiration; + this.options = options; } - /** - * Specify the {@code fields} within the hash to set an expiration for. - * - * @param fields must not be {@literal null}. - * @return new instance of {@link Expire}. - */ - public static Expire expire(List fields, Duration ttl) { + public static ExpireCommand expire(List fields, long timeout, TimeUnit unit) { Assert.notNull(fields, "Field must not be null"); - return new Expire(null, fields, ttl); + return expire(fields, Expiration.from(timeout, unit)); } - /** - * Define the {@code key} the hash is stored at. - * - * @param key must not be {@literal null}. - * @return new instance of {@link Expire}. - */ - public Expire from(ByteBuffer key) { - return new Expire(key, getFields(), ttl); + public static ExpireCommand expire(List fields, Duration ttl) { + + Assert.notNull(fields, "Field must not be null"); + return expire(fields, Expiration.from(ttl)); } - /** - * @return the ttl. - */ - public Duration getTtl() { - return ttl; + public static ExpireCommand expire(List fields, Expiration expiration) { + return new ExpireCommand(null, fields, expiration, FieldExpirationOptions.none()); + } + + public static ExpireCommand expireAt(List fields, Instant ttl, TimeUnit precision) { + + if (precision.compareTo(TimeUnit.MILLISECONDS) > 0) { + return expire(fields, Expiration.unixTimestamp(ttl.getEpochSecond(), TimeUnit.SECONDS)); + } + + return expire(fields, Expiration.unixTimestamp(ttl.toEpochMilli(), TimeUnit.MILLISECONDS)); + } + + public ExpireCommand from(ByteBuffer key) { + return new ExpireCommand(key, getFields(), expiration, options); + } + + public ExpireCommand withOptions(FieldExpirationOptions options) { + return new ExpireCommand(getKey(), getFields(), getExpiration(), options); + } + + public Expiration getExpiration() { + return expiration; + } + + public FieldExpirationOptions getOptions() { + return options; } } @@ -903,51 +911,53 @@ public Duration getTtl() { * @param key must not be {@literal null}. * @param field must not be {@literal null}. * @param duration must not be {@literal null}. - * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted already + * due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; + * {@code -2} indicating there is no such field; * @see Redis Documentation: HEXPIRE * @since 3.5 */ default Mono hExpire(ByteBuffer key, Duration duration, ByteBuffer field) { - Assert.notNull(duration, "Duration must not be null"); + Assert.notNull(duration, "Duration must not be null"); return hExpire(key, duration, Collections.singletonList(field)).singleOrEmpty(); } /** - * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. + * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has + * passed. * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. * @param duration must not be {@literal null}. - * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; * @see Redis Documentation: HEXPIRE * @since 3.5 */ default Flux hExpire(ByteBuffer key, Duration duration, List fields) { Assert.notNull(duration, "Duration must not be null"); - return hExpire(Flux.just(Expire.expire(fields, duration).from(key))) + return expireHashField(Flux.just(ExpireCommand.expire(fields, duration).from(key))) .mapNotNull(NumericResponse::getOutput); } /** - * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. + * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has + * passed. * * @param commands must not be {@literal null}. - * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; * @since 3.5 * @see Redis Documentation: HEXPIRE */ - Flux> hExpire(Publisher commands); + Flux> expireHashField(Publisher commands); /** * Expire a given {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. @@ -955,104 +965,41 @@ default Flux hExpire(ByteBuffer key, Duration duration, List f * @param key must not be {@literal null}. * @param field must not be {@literal null}. * @param duration must not be {@literal null}. - * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted already + * due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; + * {@code -2} indicating there is no such field; * @see Redis Documentation: HEXPIRE * @since 3.5 */ default Mono hpExpire(ByteBuffer key, Duration duration, ByteBuffer field) { - Assert.notNull(duration, "Duration must not be null"); + Assert.notNull(duration, "Duration must not be null"); return hpExpire(key, duration, Collections.singletonList(field)).singleOrEmpty(); } /** - * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. + * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has + * passed. * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. * @param duration must not be {@literal null}. - * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; * @see Redis Documentation: HEXPIRE * @since 3.5 */ default Flux hpExpire(ByteBuffer key, Duration duration, List fields) { - Assert.notNull(duration, "Duration must not be null"); - return hpExpire(Flux.just(Expire.expire(fields, duration).from(key))) + Assert.notNull(duration, "Duration must not be null"); + return expireHashField(Flux.just(new ExpireCommand(key, fields, + Expiration.from(duration.toMillis(), TimeUnit.MILLISECONDS), FieldExpirationOptions.none()))) .mapNotNull(NumericResponse::getOutput); } - /** - * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. - * - * @param commands must not be {@literal null}. - * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; - * @since 3.5 - * @see Redis Documentation: HEXPIRE - */ - Flux> hpExpire(Publisher commands); - - /** - * @author Tihomir Mateev - * @see Redis Documentation: HEXPIREAT - * @since 3.5 - */ - class ExpireAt extends HashFieldsCommand { - - private final Instant expireAt; - - /** - * Creates a new {@link ExpireAt} given a {@code key}, a {@link List} of {@literal fields} and a {@link Instant} - * - * @param key can be {@literal null}. - * @param fields must not be {@literal null}. - * @param expireAt the {@link Instant} to expire at. - */ - private ExpireAt(@Nullable ByteBuffer key, List fields, Instant expireAt) { - - super(key, fields); - this.expireAt = expireAt; - } - - /** - * Specify the {@code fields} within the hash to set an expiration for. - * - * @param fields must not be {@literal null}. - * @return new instance of {@link ExpireAt}. - */ - public static ExpireAt expireAt(List fields, Instant expireAt) { - - Assert.notNull(fields, "Fields must not be null"); - return new ExpireAt(null, fields, expireAt); - } - - /** - * Define the {@code key} the hash is stored at. - * - * @param key must not be {@literal null}. - * @return new instance of {@link ExpireAt}. - */ - public ExpireAt from(ByteBuffer key) { - return new ExpireAt(key, getFields(), expireAt); - } - - /** - * @return the ttl. - */ - public Instant getExpireAt() { - return expireAt; - } - } - /** * Expire a given {@literal field} in a given {@link Instant} of time, indicated as an absolute * Unix timestamp in seconds since Unix epoch @@ -1060,10 +1007,10 @@ public Instant getExpireAt() { * @param key must not be {@literal null}. * @param field must not be {@literal null}. * @param expireAt must not be {@literal null}. - * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; + * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted already + * due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is + * not met); {@code -2} indicating there is no such field; * @see Redis Documentation: HEXPIREAT * @since 3.5 */ @@ -1080,33 +1027,20 @@ default Mono hExpireAt(ByteBuffer key, Instant expireAt, ByteBuffer field) * @param key must not be {@literal null}. * @param fields must not be {@literal null}. * @param expireAt must not be {@literal null}. - * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; * @see Redis Documentation: HEXPIREAT * @since 3.5 */ default Flux hExpireAt(ByteBuffer key, Instant expireAt, List fields) { Assert.notNull(expireAt, "Duration must not be null"); - return hExpireAt(Flux.just(ExpireAt.expireAt(fields, expireAt).from(key))).mapNotNull(NumericResponse::getOutput); + return expireHashField(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.SECONDS).from(key))) + .mapNotNull(NumericResponse::getOutput); } - /** - * Expire a {@link List} of {@literal field} in a given {@link Instant} of time, indicated as an absolute - * Unix timestamp in seconds since Unix epoch - * - * @param commands must not be {@literal null}. - * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; - * @since 3.5 - * @see Redis Documentation: HEXPIREAT - */ - Flux> hExpireAt(Publisher commands); - /** * Expire a given {@literal field} in a given {@link Instant} of time, indicated as an absolute * Unix timestamp in milliseconds since Unix epoch @@ -1114,10 +1048,10 @@ default Flux hExpireAt(ByteBuffer key, Instant expireAt, List * @param key must not be {@literal null}. * @param field must not be {@literal null}. * @param expireAt must not be {@literal null}. - * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; + * @return a {@link Mono} emitting the expiration result - {@code 2} indicating the specific field is deleted already + * due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is + * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is + * not met); {@code -2} indicating there is no such field; * @see Redis Documentation: HPEXPIREAT * @since 3.5 */ @@ -1134,47 +1068,32 @@ default Mono hpExpireAt(ByteBuffer key, Instant expireAt, ByteBuffer field * @param key must not be {@literal null}. * @param fields must not be {@literal null}. * @param expireAt must not be {@literal null}. - * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; + * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; * @see Redis Documentation: HPEXPIREAT * @since 3.5 */ default Flux hpExpireAt(ByteBuffer key, Instant expireAt, List fields) { Assert.notNull(expireAt, "Duration must not be null"); - return hpExpireAt(Flux.just(ExpireAt.expireAt(fields, expireAt).from(key))).mapNotNull(NumericResponse::getOutput); + return expireHashField(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.MILLISECONDS).from(key))) + .mapNotNull(NumericResponse::getOutput); } - /** - * Expire a {@link List} of {@literal field} in a given {@link Instant} of time, indicated as an absolute - * Unix timestamp in milliseconds since Unix epoch - * - * @param commands must not be {@literal null}. - * @return a {@link Flux} emitting the expiration results one by one, {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; - * @since 3.5 - * @see Redis Documentation: HPEXPIREAT - */ - Flux> hpExpireAt(Publisher commands); - /** * Persist a given {@literal field} removing any associated expiration, measured as absolute * Unix timestamp in seconds since Unix epoch * * @param key must not be {@literal null}. * @param field must not be {@literal null}. - * @return a {@link Mono} emitting the persist result - {@code 1} indicating expiration time is removed; - * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; - * + * @return a {@link Mono} emitting the persist result - {@code 1} indicating expiration time is removed; {@code -1} + * field has no expiration time to be removed; {@code -2} indicating there is no such field; * @see Redis Documentation: HPERSIST * @since 3.5 */ default Mono hPersist(ByteBuffer key, ByteBuffer field) { - return hPersist(key, Collections.singletonList(field)).singleOrEmpty(); } @@ -1183,14 +1102,13 @@ default Mono hPersist(ByteBuffer key, ByteBuffer field) { * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a {@link Flux} emitting the persisting results one by one - {@code 1} indicating expiration time is removed; - * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; - * + * @return a {@link Flux} emitting the persisting results one by one - {@code 1} indicating expiration time is + * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such + * field; * @see Redis Documentation: HPERSIST * @since 3.5 */ default Flux hPersist(ByteBuffer key, List fields) { - return hPersist(Flux.just(new HashFieldsCommand(key, fields))).mapNotNull(NumericResponse::getOutput); } @@ -1198,9 +1116,9 @@ default Flux hPersist(ByteBuffer key, List fields) { * Persist a given {@link List} of {@literal field} removing any associated expiration. * * @param commands must not be {@literal null}. - * @return a {@link Flux} emitting the persisting results one by one - {@code 1} indicating expiration time is removed; - * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; - * * @since 3.5 + * @return a {@link Flux} emitting the persisting results one by one - {@code 1} indicating expiration time is + * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such + * field; * @since 3.5 * @see Redis Documentation: HPERSIST */ Flux> hPersist(Publisher commands); @@ -1210,9 +1128,9 @@ default Flux hPersist(ByteBuffer key, List fields) { * * @param key must not be {@literal null}. * @param field must not be {@literal null}. - * @return a {@link Mono} emitting the TTL result - the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; + * @return a {@link Mono} emitting the TTL result - the time to live in seconds; or a negative value to signal an + * error. The command returns {@code -1} if the key exists but has no associated expiration time. The command + * returns {@code -2} if the key does not exist; * @see Redis Documentation: HTTL * @since 3.5 */ @@ -1226,9 +1144,9 @@ default Mono hTtl(ByteBuffer key, ByteBuffer field) { * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a {@link Flux} emitting the TTL results one by one - the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; + * @return a {@link Flux} emitting the TTL results one by one - the time to live in seconds; or a negative value to + * signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; * @see Redis Documentation: HTTL * @since 3.5 */ @@ -1241,23 +1159,22 @@ default Flux hTtl(ByteBuffer key, List fields) { * Returns the time-to-live of all the given {@literal field} in the {@link List} in seconds. * * @param commands must not be {@literal null}. - * @return a {@link Flux} emitting the persisting results one by one - the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; + * @return a {@link Flux} emitting the persisting results one by one - the time to live in seconds; or a negative + * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration + * time. The command returns {@code -2} if the key does not exist; * @since 3.5 * @see Redis Documentation: HTTL */ Flux> hTtl(Publisher commands); - /** * Returns the time-to-live of a given {@literal field} in milliseconds. * * @param key must not be {@literal null}. * @param field must not be {@literal null}. - * @return a {@link Mono} emitting the TTL result - the time to live in milliseconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; + * @return a {@link Mono} emitting the TTL result - the time to live in milliseconds; or a negative value to signal an + * error. The command returns {@code -1} if the key exists but has no associated expiration time. The command + * returns {@code -2} if the key does not exist; * @see Redis Documentation: HPTTL * @since 3.5 */ @@ -1272,8 +1189,8 @@ default Mono hpTtl(ByteBuffer key, ByteBuffer field) { * @param key must not be {@literal null}. * @param fields must not be {@literal null}. * @return a {@link Flux} emitting the TTL results one by one - the time to live in milliseconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; * @see Redis Documentation: HPTTL * @since 3.5 */ @@ -1286,9 +1203,9 @@ default Flux hpTtl(ByteBuffer key, List fields) { * Returns the time-to-live of all the given {@literal field} in the {@link List} in milliseconds. * * @param commands must not be {@literal null}. - * @return a {@link Flux} emitting the persisting results one by one - the time to live in milliseconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; + * @return a {@link Flux} emitting the persisting results one by one - the time to live in milliseconds; or a negative + * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration + * time. The command returns {@code -2} if the key does not exist; * @since 3.5 * @see Redis Documentation: HPTTL */ diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java index 066833d52d..5fde9d5db3 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java @@ -15,11 +15,13 @@ */ package org.springframework.data.redis.connection; +import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; import org.springframework.lang.Nullable; @@ -252,38 +254,83 @@ public interface RedisHashCommands { @Nullable Long hStrLen(byte[] key, byte[] field); + default @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, + byte[]... fields) { + return expireHashField(key, expiration, FieldExpirationOptions.none(), fields); + } + + + @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, + FieldExpirationOptions options, byte[]... fields); + + /** + * Set time to live for given {@code fields} in seconds. + * + * @param key must not be {@literal null}. + * @param seconds the amount of time after which the fields will be expired in seconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable + List hExpire(byte[] key, long seconds, byte[]... fields); + /** - * Set time to live for given {@code field} in seconds. + * Set time to live for given {@code fields}. * * @param key must not be {@literal null}. - * @param seconds the amount of time after which the key will be expired in seconds, must not be {@literal null}. + * @param ttl the amount of time after which the fields will be expired in {@link Duration#toSeconds() seconds} precision, must not be {@literal null}. * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HEXPIRE - * @since 3.4 + * @since 3.5 */ @Nullable - List hExpire(byte[] key, long seconds, byte[]... fields); + default List hExpire(byte[] key, Duration ttl, byte[]... fields) { + return hExpire(key, ttl.toSeconds(), fields); + } /** - * Set time to live for given {@code field} in milliseconds. + * Set time to live for given {@code fields} in milliseconds. * * @param key must not be {@literal null}. - * @param millis the amount of time after which the key will be expired in milliseconds, must not be {@literal null}. + * @param millis the amount of time after which the fields will be expired in milliseconds, must not be {@literal null}. * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPEXPIRE - * @since 3.4 + * @since 3.5 */ @Nullable List hpExpire(byte[] key, long millis, byte[]... fields); + /** + * Set time to live for given {@code fields} in milliseconds. + * + * @param key must not be {@literal null}. + * @param ttl the amount of time after which the fields will be expired in {@link Duration#toMillis() milliseconds} precision, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted + * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; + * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); + * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HPEXPIRE + * @since 3.5 + */ + @Nullable + default List hpExpire(byte[] key, Duration ttl, byte[]... fields) { + return hpExpire(key, ttl.toMillis(), fields); + } + /** * Set the expiration for given {@code field} as a {@literal UNIX} timestamp. * @@ -295,7 +342,7 @@ public interface RedisHashCommands { * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HEXPIREAT - * @since 3.4 + * @since 3.5 */ @Nullable List hExpireAt(byte[] key, long unixTime, byte[]... fields); @@ -311,7 +358,7 @@ public interface RedisHashCommands { * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPEXPIREAT - * @since 3.4 + * @since 3.5 */ @Nullable List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields); @@ -325,27 +372,27 @@ public interface RedisHashCommands { * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; * {@literal null} when used in pipeline / transaction.{@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPERSIST - * @since 3.4 + * @since 3.5 */ @Nullable List hPersist(byte[] key, byte[]... fields); /** - * Get the time to live for {@code field} in seconds. + * Get the time to live for {@code fields} in seconds. * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * to signal an error. The command returns {@code -1} if the field exists but has no associated expiration time. + * The command returns {@code -2} if the field does not exist; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HTTL - * @since 3.4 + * @since 3.5 */ @Nullable List hTtl(byte[] key, byte[]... fields); /** - * Get the time to live for {@code field} in and convert it to the given {@link TimeUnit}. + * Get the time to live for {@code fields} in and convert it to the given {@link TimeUnit}. * * @param key must not be {@literal null}. * @param timeUnit must not be {@literal null}. @@ -354,8 +401,24 @@ public interface RedisHashCommands { * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HTTL - * @since 3.4 + * @since 3.5 */ @Nullable + // TODO: this is complete nonsense as it would jeopardize negative values + // TODO: this should be a List> List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields); + + /** + * Get the time to live for {@code fields} in milliseconds. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + List hpTtl(byte[] key, byte[]... fields); } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java index 414f178d92..49326637d3 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java @@ -16,6 +16,7 @@ package org.springframework.data.redis.connection; import java.time.Duration; +import java.time.Instant; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -191,6 +192,20 @@ default Cursor scan(KeyScanOptions options) { @Nullable Boolean expire(byte[] key, long seconds); + /** + * Set time to live for given {@code key} using {@link Duration#toSeconds() seconds} precision. + * + * @param key must not be {@literal null}. + * @param duration + * @return {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: EXPIRE + * @since 3.5 + */ + @Nullable + default Boolean expire(byte[] key, Duration duration) { + return expire(key, duration.toSeconds()); + } + /** * Set time to live for given {@code key} in milliseconds. * @@ -202,6 +217,20 @@ default Cursor scan(KeyScanOptions options) { @Nullable Boolean pExpire(byte[] key, long millis); + /** + * Set time to live for given {@code key} using {@link Duration#toMillis() milliseconds} precision. + * + * @param key must not be {@literal null}. + * @param duration + * @return {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: PEXPIRE + * @since 3.5 + */ + @Nullable + default Boolean pExpire(byte[] key, Duration duration) { + return pExpire(key, duration.toMillis()); + } + /** * Set the expiration for given {@code key} as a {@literal UNIX} timestamp. * @@ -213,6 +242,21 @@ default Cursor scan(KeyScanOptions options) { @Nullable Boolean expireAt(byte[] key, long unixTime); + /** + * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in {@link Instant#getEpochSecond() seconds} + * precision. + * + * @param key must not be {@literal null}. + * @param unixTime + * @return {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: EXPIREAT + * @since 3.5 + */ + @Nullable + default Boolean expireAt(byte[] key, Instant unixTime) { + return expireAt(key, unixTime.getEpochSecond()); + } + /** * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds. * @@ -224,6 +268,21 @@ default Cursor scan(KeyScanOptions options) { @Nullable Boolean pExpireAt(byte[] key, long unixTimeInMillis); + /** + * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in {@link Instant#toEpochMilli() + * milliseconds} precision. + * + * @param key must not be {@literal null}. + * @param unixTime + * @return {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: PEXPIREAT + * @since 3.5 + */ + @Nullable + default Boolean pExpireAt(byte[] key, Instant unixTime) { + return pExpireAt(key, unixTime.toEpochMilli()); + } + /** * Remove the expiration from given {@code key}. * diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java index f95b618cfd..ed0101641e 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -2333,6 +2333,7 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, @Nullable Long hStrLen(String key, String field); + // TODO: why why whay is this such a shitty api that there's missing all the NX, XX, GT Options /** * Set time to live for given {@code field} in seconds. * @@ -2412,7 +2413,7 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, List hPersist(String key, String... fields); /** - * Get the time to live for {@code field} in seconds. + * Get the time to live for {@code fields} in seconds. * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. @@ -2420,13 +2421,13 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HTTL - * @since 3.4 + * @since 3.5 */ @Nullable List hTtl(String key, String... fields); /** - * Get the time to live for {@code field} in and convert it to the given {@link TimeUnit}. + * Get the time to live for {@code fields} in and convert it to the given {@link TimeUnit}. * * @param key must not be {@literal null}. * @param timeUnit must not be {@literal null}. @@ -2435,11 +2436,25 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HTTL - * @since 3.4 + * @since 3.5 */ @Nullable List hTtl(String key, TimeUnit timeUnit, String... fields); + /** + * Get the time to live for {@code fields} in seconds. + * + * @param key must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in milliseconds; or a negative value + * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + List hpTtl(String key, String... fields); + // ------------------------------------------------------------------------- // Methods dealing with HyperLogLog // ------------------------------------------------------------------------- diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java index c436afaeef..3326a00d62 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.connection.jedis; +import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; @@ -26,13 +27,16 @@ import java.util.concurrent.TimeUnit; import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.RedisHashCommands; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanCursor; import org.springframework.data.redis.core.ScanIteration; import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Cluster {@link RedisHashCommands} implementation for Jedis. @@ -281,16 +285,54 @@ protected ScanIteration> doScan(CursorId cursorId, ScanOpt ScanParams params = JedisConverters.toScanParams(options); - ScanResult> result = connection.getCluster().hscan(key, - JedisConverters.toBytes(cursorId), + ScanResult> result = connection.getCluster().hscan(key, JedisConverters.toBytes(cursorId), params); return new ScanIteration<>(CursorId.of(result.getCursor()), result.getResult()); } }.open(); } + @Nullable + @Override + public List expireHashField(byte[] key, Expiration expiration, FieldExpirationOptions options, + byte[]... fields) { + + if (expiration.isPersistent()) { + return hPersist(key, fields); + } + + if (ObjectUtils.nullSafeEquals(FieldExpirationOptions.none(), options)) { + if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { + if (expiration.isUnixTimestamp()) { + return hpExpireAt(key, expiration.getExpirationTimeInMilliseconds(), fields); + } + return hpExpire(key, expiration.getExpirationTimeInMilliseconds(), fields); + } + if (expiration.isUnixTimestamp()) { + return hExpireAt(key, expiration.getExpirationTimeInSeconds(), fields); + } + return hExpire(key, expiration.getExpirationTimeInSeconds(), fields); + } + + ExpiryOption option = ExpiryOption.valueOf(options.getCondition().name()); + + if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { + if (expiration.isUnixTimestamp()) { + return connection.getCluster().hpexpireAt(key, expiration.getExpirationTimeInMilliseconds(), option, fields); + } + return connection.getCluster().hpexpire(key, expiration.getExpirationTimeInMilliseconds(), option, fields); + } + + if (expiration.isUnixTimestamp()) { + return connection.getCluster().hexpireAt(key, expiration.getExpirationTimeInSeconds(), option, fields); + } + return connection.getCluster().hexpire(key, expiration.getExpirationTimeInSeconds(), option, fields); + + } + @Override public List hExpire(byte[] key, long seconds, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); @@ -368,8 +410,19 @@ public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { try { return connection.getCluster().httl(key, fields).stream() - .map(it -> it != null ? timeUnit.convert(it, TimeUnit.SECONDS) : null) - .toList(); + .map(it -> it != null ? timeUnit.convert(it, TimeUnit.SECONDS) : null).toList(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public List hpTtl(byte[] key, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); + Assert.notNull(fields, "Fields must not be null"); + + try { + return connection.getCluster().hpttl(key, fields); } catch (Exception ex) { throw convertJedisAccessException(ex); } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java index 887412bb09..e8751e85cb 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java @@ -16,6 +16,7 @@ package org.springframework.data.redis.connection.jedis; import redis.clients.jedis.Jedis; +import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.commands.PipelineBinaryCommands; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; @@ -28,6 +29,7 @@ import java.util.concurrent.TimeUnit; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.RedisHashCommands; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.core.Cursor; @@ -37,6 +39,7 @@ import org.springframework.data.redis.core.ScanOptions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * {@link RedisHashCommands} implementation for Jedis. @@ -152,7 +155,8 @@ public List> hRandFieldWithValues(byte[] key, long count) List> convertedMapEntryList = new ArrayList<>(mapEntryList.size()); - mapEntryList.forEach(entry -> convertedMapEntryList.add(Converters.entryOf(entry.getKey(), entry.getValue()))); + mapEntryList + .forEach(entry -> convertedMapEntryList.add(Converters.entryOf(entry.getKey(), entry.getValue()))); return convertedMapEntryList; @@ -239,8 +243,8 @@ protected ScanIteration> doScan(byte[] key, CursorId curso ScanParams params = JedisConverters.toScanParams(options); - ScanResult> result = connection.getJedis().hscan(key, - JedisConverters.toBytes(cursorId), params); + ScanResult> result = connection.getJedis().hscan(key, JedisConverters.toBytes(cursorId), + params); return new ScanIteration<>(CursorId.of(result.getCursor()), result.getResult()); } @@ -262,6 +266,46 @@ public List hpExpire(byte[] key, long millis, byte[]... fields) { return connection.invoke().just(Jedis::hpexpire, PipelineBinaryCommands::hpexpire, key, millis, fields); } + @Override + public @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, + FieldExpirationOptions options, byte[]... fields) { + + if (expiration.isPersistent()) { + return hPersist(key, fields); + } + + if (ObjectUtils.nullSafeEquals(FieldExpirationOptions.none(), options)) { + if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { + if (expiration.isUnixTimestamp()) { + return hpExpireAt(key, expiration.getExpirationTimeInMilliseconds(), fields); + } + return hpExpire(key, expiration.getExpirationTimeInMilliseconds(), fields); + } + if (expiration.isUnixTimestamp()) { + return hExpireAt(key, expiration.getExpirationTimeInSeconds(), fields); + } + return hExpire(key, expiration.getExpirationTimeInSeconds(), fields); + } + + ExpiryOption option = ExpiryOption.valueOf(options.getCondition().name()); + + if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { + if (expiration.isUnixTimestamp()) { + return connection.invoke().just(Jedis::hpexpireAt, PipelineBinaryCommands::hpexpireAt, key, + expiration.getExpirationTimeInMilliseconds(), option, fields); + } + return connection.invoke().just(Jedis::hpexpire, PipelineBinaryCommands::hpexpire, key, + expiration.getExpirationTimeInMilliseconds(), option, fields); + } + + if (expiration.isUnixTimestamp()) { + return connection.invoke().just(Jedis::hexpireAt, PipelineBinaryCommands::hexpireAt, key, + expiration.getExpirationTimeInSeconds(), option, fields); + } + return connection.invoke().just(Jedis::hexpire, PipelineBinaryCommands::hexpire, key, + expiration.getExpirationTimeInSeconds(), option, fields); + } + @Override public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { return connection.invoke().just(Jedis::hexpireAt, PipelineBinaryCommands::hexpireAt, key, unixTime, fields); @@ -269,7 +313,8 @@ public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { @Override public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { - return connection.invoke().just(Jedis::hpexpireAt, PipelineBinaryCommands::hpexpireAt, key, unixTimeInMillis, fields); + return connection.invoke().just(Jedis::hpexpireAt, PipelineBinaryCommands::hpexpireAt, key, unixTimeInMillis, + fields); } @Override @@ -288,6 +333,11 @@ public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { .toList(Converters.secondsToTimeUnit(timeUnit)); } + @Override + public List hpTtl(byte[] key, byte[]... fields) { + return connection.invoke().just(Jedis::hpttl, PipelineBinaryCommands::hpttl, key, fields); + } + @Nullable @Override public Long hStrLen(byte[] key, byte[] field) { diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java index 01e683daa4..16564fd1eb 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java @@ -15,10 +15,12 @@ */ package org.springframework.data.redis.connection.lettuce; +import io.lettuce.core.ExpireArgs; import io.lettuce.core.KeyValue; import io.lettuce.core.MapScanCursor; import io.lettuce.core.ScanArgs; import io.lettuce.core.api.async.RedisHashAsyncCommands; +import io.lettuce.core.protocol.CommandArgs; import java.util.List; import java.util.Map; @@ -27,6 +29,7 @@ import java.util.concurrent.TimeUnit; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.RedisHashCommands; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.core.Cursor; @@ -36,6 +39,7 @@ import org.springframework.data.redis.core.ScanOptions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * @author Christoph Strobl @@ -210,6 +214,46 @@ public Cursor> hScan(byte[] key, ScanOptions options) { return hScan(key, CursorId.initial(), options); } + @Override + public @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, + FieldExpirationOptions options, byte[]... fields) { + + if (expiration.isPersistent()) { + return hPersist(key, fields); + } + + ExpireArgs option = new ExpireArgs() { + @Override + public void build(CommandArgs args) { + + if(ObjectUtils.nullSafeEquals(options, FieldExpirationOptions.none())) { + return; + } + + args.add(options.getCondition().name()); + } + }; + + if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { + if (expiration.isUnixTimestamp()) { + return connection.invoke().fromMany(RedisHashAsyncCommands::hpexpireat, key, + expiration.getExpirationTimeInMilliseconds(), option, fields).toList(); + } + return connection.invoke() + .fromMany(RedisHashAsyncCommands::hpexpire, key, expiration.getExpirationTimeInMilliseconds(), option, fields) + .toList(); + } + + if (expiration.isUnixTimestamp()) { + return connection.invoke() + .fromMany(RedisHashAsyncCommands::hexpireat, key, expiration.getExpirationTimeInSeconds(), option, fields) + .toList(); + } + return connection.invoke() + .fromMany(RedisHashAsyncCommands::hexpire, key, expiration.getExpirationTimeInSeconds(), option, fields) + .toList(); + } + @Override public List hExpire(byte[] key, long seconds, byte[]... fields) { return connection.invoke().fromMany(RedisHashAsyncCommands::hexpire, key, seconds, fields).toList(); @@ -246,6 +290,11 @@ public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { .toList(Converters.secondsToTimeUnit(timeUnit)); } + @Override + public List hpTtl(byte[] key, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::hpttl, key, fields).toList(); + } + /** * @param key * @param cursorId diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java index 33e9c162e1..3cc7bfd9c3 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java @@ -15,8 +15,10 @@ */ package org.springframework.data.redis.connection.lettuce; +import io.lettuce.core.ExpireArgs; import io.lettuce.core.KeyValue; import io.lettuce.core.ScanStream; +import io.lettuce.core.protocol.CommandArgs; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -25,10 +27,11 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.reactivestreams.Publisher; - +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.ReactiveHashCommands; import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; @@ -38,6 +41,7 @@ import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * @author Christoph Strobl @@ -265,49 +269,48 @@ public Flux> hStrLen(Publisher> hExpire(Publisher commands) { + public Flux> expireHashField(Publisher commands) { return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKey(), "Key must not be null"); Assert.notNull(command.getFields(), "Fields must not be null"); - return cmd.hexpire(command.getKey(), command.getTtl().toSeconds(), command.getFields().toArray(ByteBuffer[]::new)) - .map(value -> new NumericResponse<>(command, value)); - })); - } - - @Override - public Flux> hpExpire(Publisher commands) { - return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + ByteBuffer[] fields = command.getFields().toArray(ByteBuffer[]::new); - Assert.notNull(command.getKey(), "Key must not be null"); - Assert.notNull(command.getFields(), "Fields must not be null"); + if (command.getExpiration().isPersistent()) { + return cmd.hpersist(command.getKey(), fields).map(value -> new NumericResponse<>(command, value)); + } - return cmd.hpexpire(command.getKey(), command.getTtl().toMillis(), command.getFields().toArray(ByteBuffer[]::new)) - .map(value -> new NumericResponse<>(command, value)); - })); - } + ExpireArgs args = new ExpireArgs() { - @Override - public Flux> hExpireAt(Publisher commands) { - return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + @Override + public void build(CommandArgs args) { + super.build(args); + if (ObjectUtils.nullSafeEquals(command.getOptions(), FieldExpirationOptions.none())) { + return; + } - Assert.notNull(command.getKey(), "Key must not be null"); - Assert.notNull(command.getFields(), "Fields must not be null"); + args.add(command.getOptions().getCondition().name()); + } + }; - return cmd.hexpireat(command.getKey(), command.getExpireAt().getEpochSecond(), command.getFields().toArray(ByteBuffer[]::new)) - .map(value -> new NumericResponse<>(command, value)); - })); - } + if (command.getExpiration().isUnixTimestamp()) { - @Override - public Flux> hpExpireAt(Publisher commands) { - return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + if (command.getExpiration().getTimeUnit().equals(TimeUnit.MILLISECONDS)) { + return cmd + .hpexpireat(command.getKey(), command.getExpiration().getExpirationTimeInMilliseconds(), args, fields) + .map(value -> new NumericResponse<>(command, value)); + } + return cmd.hexpireat(command.getKey(), command.getExpiration().getExpirationTimeInSeconds(), args, fields) + .map(value -> new NumericResponse<>(command, value)); + } - Assert.notNull(command.getKey(), "Key must not be null"); - Assert.notNull(command.getFields(), "Fields must not be null"); + if (command.getExpiration().getTimeUnit().equals(TimeUnit.MILLISECONDS)) { + return cmd.hpexpire(command.getKey(), command.getExpiration().getExpirationTimeInMilliseconds(), args, fields) + .map(value -> new NumericResponse<>(command, value)); + } - return cmd.hpexpireat(command.getKey(), command.getExpireAt().toEpochMilli(), command.getFields().toArray(ByteBuffer[]::new)) + return cmd.hexpire(command.getKey(), command.getExpiration().getExpirationTimeInSeconds(), args, fields) .map(value -> new NumericResponse<>(command, value)); })); } diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java index ff9e5b1300..0503c33094 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java @@ -23,6 +23,8 @@ import java.util.Set; import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; /** @@ -157,6 +159,12 @@ public interface BoundHashOperations extends BoundKeyOperations { @Nullable Long lengthOfValue(HK hashKey); + default ExpireChanges expire(Expiration expiration, Collection hashKeys) { + return expire(expiration, FieldExpirationOptions.none(), hashKeys); + } + + ExpireChanges expire(Expiration expiration, FieldExpirationOptions options, Collection hashKeys); + /** * Set time to live for given {@code hashKey} (aka field). * @@ -171,7 +179,7 @@ public interface BoundHashOperations extends BoundKeyOperations { * @since 3.5 */ @Nullable - List expire(Duration timeout, Collection hashKeys); + ExpireChanges expire(Duration timeout, Collection hashKeys); /** * Set the expiration for given {@code hashKey} (aka field) as a {@literal date} timestamp. @@ -187,7 +195,7 @@ public interface BoundHashOperations extends BoundKeyOperations { * @since 3.5 */ @Nullable - List expireAt(Instant expireAt, Collection hashKeys); + ExpireChanges expireAt(Instant expireAt, Collection hashKeys); /** * Remove the expiration from given {@code hashKey} (aka field). @@ -200,7 +208,7 @@ public interface BoundHashOperations extends BoundKeyOperations { * @since 3.5 */ @Nullable - List persist(Collection hashKeys); + ExpireChanges persist(Collection hashKeys); /** * Get the time to live for {@code hashKey} (aka field) in seconds. @@ -213,7 +221,7 @@ public interface BoundHashOperations extends BoundKeyOperations { * @since 3.5 */ @Nullable - List getExpire(Collection hashKeys); + Expirations getExpire(Collection hashKeys); /** * Get the time to live for {@code hashKey} (aka field) and convert it to the given {@link TimeUnit}. @@ -227,7 +235,7 @@ public interface BoundHashOperations extends BoundKeyOperations { * @since 3.5 */ @Nullable - List getExpire(TimeUnit timeUnit, Collection hashKeys); + Expirations getExpire(TimeUnit timeUnit, Collection hashKeys); /** * Get size of hash at the bound key. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java index 5df4422e48..2be7e0bd3f 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java @@ -27,7 +27,10 @@ import java.util.concurrent.TimeUnit; import org.springframework.core.convert.converter.Converter; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.convert.Converters; +import org.springframework.data.redis.core.Expirations.Timeouts; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -215,44 +218,88 @@ public Boolean putIfAbsent(K key, HK hashKey, HV value) { } @Override - public List expire(K key, Duration duration, Collection hashKeys) { + public ExpireChanges expire(K key, Duration duration, Collection hashKeys) { + + List orderedKeys = List.copyOf(hashKeys); + byte[] rawKey = rawKey(key); - byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); - long rawTimeout = duration.toMillis(); + byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); + boolean splitSecond = TimeoutUtils.hasMillis(duration); + + List raw = execute(connection -> { + if (splitSecond) { + return connection.hashCommands().hpExpire(rawKey, duration.toMillis(), rawHashKeys); + } + return connection.hashCommands().hExpire(rawKey, TimeoutUtils.toSeconds(duration), rawHashKeys); + }); - return execute(connection -> connection.hpExpire(rawKey, rawTimeout, rawHashKeys)); + return raw != null ? ExpireChanges.of(orderedKeys, raw) : null; } @Override - public List expireAt(K key, Instant instant, Collection hashKeys) { + public ExpireChanges expireAt(K key, Instant instant, Collection hashKeys) { + + List orderedKeys = List.copyOf(hashKeys); + byte[] rawKey = rawKey(key); - byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); + byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); - return execute(connection -> connection.hpExpireAt(rawKey, instant.toEpochMilli(), rawHashKeys)); + Long millis = instant.toEpochMilli(); + + List raw = execute(connection -> TimeoutUtils.containsSplitSecond(millis) + ? connection.hashCommands().hpExpireAt(rawKey, millis, rawHashKeys) + : connection.hashCommands().hExpireAt(rawKey, instant.getEpochSecond(), rawHashKeys)); + + return raw != null ? ExpireChanges.of(orderedKeys, raw) : null; } @Override - public List persist(K key, Collection hashKeys) { + public ExpireChanges expire(K key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys) { + + List orderedKeys = List.copyOf(hashKeys); + byte[] rawKey = rawKey(key); - byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); + byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); + List raw = execute(connection -> connection.hashCommands().expireHashField(rawKey, expiration, options, rawHashKeys)); - return execute(connection -> connection.hPersist(rawKey, rawHashKeys)); + return raw != null ? ExpireChanges.of(orderedKeys, raw) : null; } @Override - public List getExpire(K key, Collection hashKeys) { + public ExpireChanges persist(K key, Collection hashKeys) { + + List orderedKeys = List.copyOf(hashKeys); + byte[] rawKey = rawKey(key); - byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); + byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); + + List raw = execute(connection -> connection.hashCommands().hPersist(rawKey, rawHashKeys)); - return execute(connection -> connection.hTtl(rawKey, rawHashKeys)); + return raw != null ? ExpireChanges.of(orderedKeys, raw) : null; } @Override - public List getExpire(K key, TimeUnit timeUnit, Collection hashKeys) { + public Expirations getExpire(K key, TimeUnit timeUnit, Collection hashKeys) { + + if(timeUnit.compareTo(TimeUnit.MILLISECONDS) < 0) { + throw new IllegalArgumentException("%s precision is not supported must be >= MILLISECONDS".formatted(timeUnit)); + } + + List orderedKeys = List.copyOf(hashKeys); + byte[] rawKey = rawKey(key); - byte[][] rawHashKeys = rawHashKeys(hashKeys.toArray()); + byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); + + List raw = execute( + connection -> TimeUnit.MILLISECONDS.equals(timeUnit) ? connection.hashCommands().hpTtl(rawKey, rawHashKeys) + : connection.hashCommands().hTtl(rawKey, timeUnit, rawHashKeys)); + + if (raw == null) { + return null; + } - return execute(connection -> connection.hTtl(rawKey, timeUnit, rawHashKeys)); + Timeouts timeouts = new Timeouts(TimeUnit.MILLISECONDS.equals(timeUnit) ? timeUnit : TimeUnit.SECONDS, raw); + return Expirations.of(timeUnit, orderedKeys, timeouts); } @Override diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java index c3e004c25d..d373a7f063 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java @@ -15,20 +15,28 @@ */ package org.springframework.data.redis.core; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ReactiveHashCommands.ExpireCommand; +import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; +import org.springframework.data.redis.core.types.Expiration; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.nio.ByteBuffer; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import org.reactivestreams.Publisher; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.ReactiveHashCommands; import org.springframework.data.redis.connection.convert.Converters; +import org.springframework.data.redis.core.Expirations.Timeouts; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -63,8 +71,7 @@ public Mono remove(H key, Object... hashKeys) { Assert.noNullElements(hashKeys, "Hash keys must not contain null elements"); return createMono(hashCommands -> Flux.fromArray(hashKeys) // - .map(hashKey -> (HK) hashKey) - .map(this::rawHashKey) // + .map(hashKey -> (HK) hashKey).map(this::rawHashKey) // .collectList() // .flatMap(hks -> hashCommands.hDel(rawKey(key), hks))); } @@ -86,8 +93,8 @@ public Mono get(H key, Object hashKey) { Assert.notNull(key, "Key must not be null"); Assert.notNull(hashKey, "Hash key must not be null"); - return createMono(hashCommands -> hashCommands.hGet(rawKey(key), rawHashKey((HK) hashKey)) - .map(this::readHashValue)); + return createMono( + hashCommands -> hashCommands.hGet(rawKey(key), rawHashKey((HK) hashKey)).map(this::readHashValue)); } @Override @@ -109,8 +116,8 @@ public Mono increment(H key, HK hashKey, long delta) { Assert.notNull(key, "Key must not be null"); Assert.notNull(hashKey, "Hash key must not be null"); - return template.doCreateMono(connection -> connection.numberCommands() - .hIncrBy(rawKey(key), rawHashKey(hashKey), delta)); + return template + .doCreateMono(connection -> connection.numberCommands().hIncrBy(rawKey(key), rawHashKey(hashKey), delta)); } @Override @@ -119,8 +126,8 @@ public Mono increment(H key, HK hashKey, double delta) { Assert.notNull(key, "Key must not be null"); Assert.notNull(hashKey, "Hash key must not be null"); - return template.doCreateMono(connection -> connection.numberCommands() - .hIncrBy(rawKey(key), rawHashKey(hashKey), delta)); + return template + .doCreateMono(connection -> connection.numberCommands().hIncrBy(rawKey(key), rawHashKey(hashKey), delta)); } @Override @@ -137,8 +144,7 @@ public Mono> randomEntry(H key) { Assert.notNull(key, "Key must not be null"); - return createMono(hashCommands -> hashCommands.hRandFieldWithValues(rawKey(key))) - .map(this::deserializeHashEntry); + return createMono(hashCommands -> hashCommands.hRandFieldWithValues(rawKey(key))).map(this::deserializeHashEntry); } @Override @@ -235,6 +241,78 @@ public Flux> scan(H key, ScanOptions options) { .map(this::deserializeHashEntry)); } + @Override + public Mono> expire(H key, Duration timeout, Collection hashKeys) { + return expire(key, Expiration.from(timeout), FieldExpirationOptions.none(), hashKeys); + } + + @Override + public Mono> expire(H key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys) { + + List orderedKeys = List.copyOf(hashKeys); + ByteBuffer rawKey = rawKey(key); + List rawHashKeys = orderedKeys.stream().map(this::rawHashKey).toList(); + + Mono> raw =createFlux(connection -> { + return connection.expireHashField(Mono.just(ExpireCommand.expire(rawHashKeys, expiration).from(rawKey).withOptions(options))).map(NumericResponse::getOutput); + }).collectList(); + + return raw.map(values -> ExpireChanges.of(orderedKeys, values)); + } + + @Nullable + @Override + public Mono> expireAt(H key, Instant expireAt, Collection hashKeys) { + + List orderedKeys = List.copyOf(hashKeys); + ByteBuffer rawKey = rawKey(key); + List rawHashKeys = orderedKeys.stream().map(this::rawHashKey).toList(); + + Mono> raw = createFlux(connection -> connection.hExpireAt(rawKey, expireAt, rawHashKeys)).collectList(); + + return raw.map(values -> ExpireChanges.of(orderedKeys, values)); + } + + @Nullable + @Override + public Mono> persist(H key, Collection hashKeys) { + + List orderedKeys = List.copyOf(hashKeys); + ByteBuffer rawKey = rawKey(key); + List rawHashKeys = orderedKeys.stream().map(this::rawHashKey).toList(); + + Mono> raw = createFlux(connection -> connection.hPersist(rawKey, rawHashKeys)).collectList(); + + return raw.map(values -> ExpireChanges.of(orderedKeys, values)); + } + + @Nullable + @Override + public Mono> getExpire(H key, TimeUnit timeUnit, Collection hashKeys) { + + if (timeUnit.compareTo(TimeUnit.MILLISECONDS) < 0) { + throw new IllegalArgumentException("%s precision is not supported must be >= MILLISECONDS".formatted(timeUnit)); + } + + List orderedKeys = List.copyOf(hashKeys); + ByteBuffer rawKey = rawKey(key); + List rawHashKeys = orderedKeys.stream().map(this::rawHashKey).toList(); + + Mono> raw = createFlux(connection -> { + + if (TimeUnit.MILLISECONDS.equals(timeUnit)) { + return connection.hpTtl(rawKey, rawHashKeys); + } + return connection.hTtl(rawKey, rawHashKeys); + }).collectList(); + + return raw.map(values -> { + + Timeouts timeouts = new Timeouts(TimeUnit.MILLISECONDS.equals(timeUnit) ? timeUnit : TimeUnit.SECONDS, values); + return Expirations.of(timeUnit, orderedKeys, timeouts); + }); + } + @Override public Mono delete(H key) { diff --git a/src/main/java/org/springframework/data/redis/core/Expirations.java b/src/main/java/org/springframework/data/redis/core/Expirations.java new file mode 100644 index 0000000000..958f90e3a8 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/core/Expirations.java @@ -0,0 +1,303 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.core; + +import java.time.Duration; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +/** + * Value Object linking a number of keys to their {@link Expiration} retaining the order of the original source. + * Dedicated higher level methods interpret raw expiration values retrieved from a Redis Client. + *
        + *
      1. {@link #persistent()} returns keys that do not have an associated time to live
      2. + *
      3. {@link #missing()} returns keys that do not exist and therefore have no associated time to live
      4. + *
      5. {@link #expirations()} returns the ordered list of {@link Expiration expirations} based on the raw values
      6. + *
      7. {@link #expiring()} returns the expiring keys along with their {@link Duration time to live}
      8. + *
      + * + * @author Christoph Strobl + * @since 3.5 + */ +public class Expirations { // TODO: should we move this to let's say Hash.class or another place + + private final TimeUnit unit; + private final Map expirations; + + Expirations(TimeUnit unit, Map expirations) { + this.unit = unit; + this.expirations = expirations; + } + + /** + * Factory Method to create {@link Expirations} from raw sources provided in a given {@link TimeUnit}. + * + * @param targetUnit the actual time unit of the raw timeToLive values. + * @param keys the keys to associated with the raw values in timeToLive. Defines the actual order of entries within + * {@link Expirations}. + * @param timeouts the raw Redis time to live values. + * @return new instance of {@link Expirations}. + * @param the key type used + */ + public static Expirations of(TimeUnit targetUnit, List keys, Timeouts timeouts) { + + if (keys.size() != timeouts.size()) { + throw new IllegalArgumentException( + "Keys and Timeouts must be of same size but was %s vs %s".formatted(keys.size(), timeouts.size())); + } + if (keys.size() == 1) { + return new Expirations<>(targetUnit, + Map.of(keys.iterator().next(), Expiration.of(timeouts.raw().iterator().next(), timeouts.timeUnit()))); + } + + Map target = CollectionUtils.newLinkedHashMap(keys.size()); + for (int i = 0; i < keys.size(); i++) { + target.put(keys.get(i), Expiration.of(timeouts.get(i), timeouts.timeUnit())); + } + return new Expirations<>(targetUnit, target); + } + + /** + * @return an ordered set of keys that do not have a time to live. + */ + public Set persistent() { + return filterByState(Expiration.PERSISTENT); + } + + /** + * @return an ordered set of keys that do not exists and therefore do not have a time to live. + */ + public Set missing() { + return filterByState(Expiration.MISSING); + } + + /** + * @return an ordered set of all {@link Expirations expirations} where the {@link Expiration#value()} is using the + * {@link TimeUnit} defined in {@link #precision()}. + */ + public List expirations() { + return expirations.values().stream().map(it -> it.convert(this.unit)).toList(); + } + + /** + * @return the {@link TimeUnit} for {@link Expiration expirations} held by this instance. + */ + public TimeUnit precision() { + return unit; + } + + /** + * @return an ordered {@link List} of {@link java.util.Map.Entry entries} combining keys with their actual time to + * live. {@link Expiration#isMissing() Missing} and {@link Expiration#isPersistent() persistent} entries are + * skipped. + */ + public List> expiring() { + return expirations.entrySet().stream().filter(it -> !it.getValue().isMissing() && !it.getValue().isPersistent()) + .map(it -> Map.entry(it.getKey(), toDuration(it.getValue()))).toList(); + } + + /** + * @param key + * @return the {@link Expirations expirations} where the {@link Expiration#value()} is using the {@link TimeUnit} + * defined in {@link #precision()} or {@literal null} if no entry could be found. + */ + @Nullable + public Expiration expirationOf(K key) { + + Expiration expiration = expirations.get(key); + if (expiration == null) { + return null; + } + + return expiration.convert(this.unit); + } + + /** + * @param key + * @return the time to live value of the requested key if it exists and the expiration is neither + * {@link Expiration#isMissing() missing} nor {@link Expiration#isPersistent() persistent}, {@literal null} + * otherwise. + */ + @Nullable + public Duration ttlOf(K key) { + + Expiration expiration = expirationOf(key); + if (expiration == null) { + return null; + } + return toDuration(expiration); + } + + private Set filterByState(Expiration filter) { + return expirations.entrySet().stream().filter(entry -> entry.getValue().equals(filter)).map(Map.Entry::getKey) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + @Nullable + static Duration toDuration(Expiration expiration) { + + if (expiration.sourceUnit == null) { + return null; + } + return Duration.of(expiration.raw(), expiration.sourceUnit.toChronoUnit()); + } + + public record Timeouts(TimeUnit timeUnit, List raw) { + + Long get(int index) { + return raw.get(index); + } + + public int size() { + return raw.size(); + } + } + + /** + * Expiration holds time to live {@link #raw()} values as returned by a Redis Client. {@link #value()} serves the + * actual timeout in the given temporal context converting the {@link #raw()} value into a target {@link TimeUnit}. + * Dedicated methods such as {@link #isPersistent()} allow interpretation of the raw result. {@link #MISSING} and + * {@link #PERSISTENT} mark predefined states returned by Redis indicating a time to live value could not be retrieved + * due to various reasons. + */ + public static class Expiration { // TODO: is Expiry a better name for this type? + + private final long raw; + @Nullable TimeUnit sourceUnit; + @Nullable TimeUnit targetUnit; + + public Expiration(long value) { + this(value, null); + } + + public Expiration(long value, @Nullable TimeUnit sourceUnit) { + this(value, sourceUnit, null); + } + + public Expiration(long value, @Nullable TimeUnit sourceUnit, @Nullable TimeUnit targetUnit) { + this.raw = value; + this.sourceUnit = sourceUnit; + this.targetUnit = targetUnit; + } + + /** + * The raw source value as returned by the Redis Client. + * + * @return the raw data + */ + public long raw() { + return raw; + } + + /** + * @return the {@link #raw()} value converted into the {@link #convert(TimeUnit) requested} target {@link TimeUnit}. + */ + public long value() { + + if (sourceUnit == null || targetUnit == null) { + return raw; + } + return targetUnit.convert(raw, sourceUnit); + } + + /** + * @param timeUnit must not be {@literal null}. + * @return the {@link Expiration} instance with new target {@link TimeUnit} set for obtaining the {@link #value() + * value}, or the same instance raw value cannot or must not be converted. + */ + public Expiration convert(TimeUnit timeUnit) { + + if (sourceUnit == null || ObjectUtils.nullSafeEquals(sourceUnit, timeUnit)) { + return this; + } + return new Expiration(raw, sourceUnit, timeUnit); + } + + /** + * Predefined {@link Expiration} for a key that does not exists and therefore does not have a time to live. + */ + public static Expiration MISSING = new Expiration(-2L); + + /** + * Predefined {@link Expiration} for a key that exists but does not expire. + */ + public static Expiration PERSISTENT = new Expiration(-1L); + + /** + * @return {@literal true} if key exists but does not expire. + */ + public boolean isPersistent() { + return PERSISTENT.equals(this); + } + + /** + * @return {@literal true} if key does not exists and therefore does not have a time to live. + */ + public boolean isMissing() { + return MISSING.equals(this); + } + + /** + * Factory method for creating {@link Expiration} instances, returning predefined ones if the value matches a known + * reserved state. + * + * @return the {@link Expiration} for the given raw value. + */ + static Expiration of(Number value, TimeUnit timeUnit) { + return switch (value.intValue()) { + case -2 -> MISSING; + case -1 -> PERSISTENT; + default -> new Expiration(value.longValue(), timeUnit); + }; + } + + @Override + public boolean equals(Object o) { + + if (o == this) { + return true; + } + + if (!(o instanceof Expiration that)) { + return false; + } + + if (!ObjectUtils.nullSafeEquals(this.sourceUnit, that.sourceUnit)) { + return false; + } + + if (!ObjectUtils.nullSafeEquals(this.targetUnit, that.targetUnit)) { + return false; + } + + return this.raw == that.raw; + } + + @Override + public int hashCode() { + return Objects.hash(raw); + } + } +} diff --git a/src/main/java/org/springframework/data/redis/core/ExpireChanges.java b/src/main/java/org/springframework/data/redis/core/ExpireChanges.java new file mode 100644 index 0000000000..b9486f639d --- /dev/null +++ b/src/main/java/org/springframework/data/redis/core/ExpireChanges.java @@ -0,0 +1,195 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.core; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.springframework.util.CollectionUtils; + +/** + * Value Object linking a number of keys to their {@link ExpiryChangeState} retaining the order of the original source. + * Dedicated higher level methods interpret raw values retrieved from a Redis Client. + *
        + *
      1. {@link #ok()} returns keys for which the time to live has been set
      2. + *
      3. {@link #expired()} returns keys that have been expired
      4. + *
      5. {@link #missed()} returns keys for which the time to live could not be set because they do not exist
      6. + *
      7. {@link #skipped()} returns keys for which the time to live has not been set because a precondition was not + * met
      8. + *
      + * + * @author Christoph Strobl + * @since 3.5 + */ +public class ExpireChanges { + + private final Map changes; + + ExpireChanges(Map changes) { + this.changes = changes; + } + + /** + * Factory Method to create {@link ExpireChanges} from raw sources. + * + * @param keys the keys to associated with the raw values in states. Defines the actual order of entries within + * {@link ExpireChanges}. + * @param states the raw Redis state change values. + * @return new instance of {@link ExpireChanges}. + * @param the key type used + */ + public static ExpireChanges of(List keys, List states) { + + if (keys.size() == 1) { + return new ExpireChanges<>(Map.of(keys.iterator().next(), stateFromValue(states.iterator().next()))); + } + + Map target = CollectionUtils.newLinkedHashMap(keys.size()); + for (int i = 0; i < keys.size(); i++) { + target.put(keys.get(i), stateFromValue(states.get(i))); + } + return new ExpireChanges<>(target); + } + + /** + * @return an ordered {@link List} of the status changes. + */ + public List stateChanges() { + return List.copyOf(changes.values()); + } + + /** + * @return the status change for the given {@literal key}, or {@literal null} if {@link ExpiryChangeState} does not + * contain an entry for it. + */ + public ExpiryChangeState stateOf(K key) { + return changes.get(key); + } + + /** + * @return {@literal true} if all changes are {@link ExpiryChangeState#OK}. + */ + public boolean allOk() { + return allMach(ExpiryChangeState.OK::equals); + } + + /** + * @return {@literal true} if all changes are either ok {@link ExpiryChangeState#OK} or + * {@link ExpiryChangeState#EXPIRED}. + */ + public boolean allChanged() { + return allMach(it -> ExpiryChangeState.OK.equals(it) || ExpiryChangeState.EXPIRED.equals(it)); + } + + /** + * @return an ordered list of if all changes are {@link ExpiryChangeState#OK}. + */ + public Set ok() { + return filterByState(ExpiryChangeState.OK); + } + + /** + * @return an ordered list of if all changes are {@link ExpiryChangeState#EXPIRED}. + */ + public Set expired() { + return filterByState(ExpiryChangeState.EXPIRED); + } + + /** + * @return an ordered list of if all changes are {@link ExpiryChangeState#DOES_NOT_EXIST}. + */ + public Set missed() { + return filterByState(ExpiryChangeState.DOES_NOT_EXIST); + } + + /** + * @return an ordered list of if all changes are {@link ExpiryChangeState#CONDITION_NOT_MET}. + */ + public Set skipped() { + return filterByState(ExpiryChangeState.CONDITION_NOT_MET); + } + + public boolean allMach(Predicate predicate) { + return changes.values().stream().allMatch(predicate); + } + + private Set filterByState(ExpiryChangeState filter) { + return changes.entrySet().stream().filter(entry -> entry.getValue().equals(filter)).map(Map.Entry::getKey) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + private static ExpiryChangeState stateFromValue(Number value) { + return ExpiryChangeState.of(value); + } + + public record ExpiryChangeState(long value) { + + public static final ExpiryChangeState DOES_NOT_EXIST = new ExpiryChangeState(-2L); + public static final ExpiryChangeState CONDITION_NOT_MET = new ExpiryChangeState(0L); + public static final ExpiryChangeState OK = new ExpiryChangeState(1L); + public static final ExpiryChangeState EXPIRED = new ExpiryChangeState(2L); + + static ExpiryChangeState of(Number value) { + return switch (value.intValue()) { + case -2 -> DOES_NOT_EXIST; + case 0 -> CONDITION_NOT_MET; + case 1 -> OK; + case 2 -> EXPIRED; + default -> new ExpiryChangeState(value.longValue()); + }; + } + + public boolean isOk() { + return OK.equals(this); + } + + public boolean isExpired() { + return EXPIRED.equals(this); + } + + public boolean isMissing() { + return DOES_NOT_EXIST.equals(this); + } + + public boolean isSkipped() { + return CONDITION_NOT_MET.equals(this); + } + + @Override + public boolean equals(Object o) { + + if (o == this) { + return true; + } + + if (!(o instanceof ExpiryChangeState that)) { + return false; + } + + return this.value == that.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + } +} diff --git a/src/main/java/org/springframework/data/redis/core/HashOperations.java b/src/main/java/org/springframework/data/redis/core/HashOperations.java index ea17d26e2d..c32c33983c 100644 --- a/src/main/java/org/springframework/data/redis/core/HashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/HashOperations.java @@ -23,6 +23,8 @@ import java.util.Set; import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; /** @@ -240,7 +242,7 @@ public interface HashOperations { * @since 3.5 */ @Nullable - List expire(H key, Duration timeout, Collection hashKeys); + ExpireChanges expire(H key, Duration timeout, Collection hashKeys); /** * Set the expiration for given {@code hashKey} (aka field) as a {@literal date} timestamp. @@ -257,7 +259,9 @@ public interface HashOperations { * @since 3.5 */ @Nullable - List expireAt(H key, Instant expireAt, Collection hashKeys); + ExpireChanges expireAt(H key, Instant expireAt, Collection hashKeys); + + ExpireChanges expire(H key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys); /** * Remove the expiration from given {@code hashKey} (aka field). @@ -271,7 +275,7 @@ public interface HashOperations { * @since 3.5 */ @Nullable - List persist(H key, Collection hashKeys); + ExpireChanges persist(H key, Collection hashKeys); /** * Get the time to live for {@code hashKey} (aka field) in seconds. @@ -285,7 +289,9 @@ public interface HashOperations { * @since 3.5 */ @Nullable - List getExpire(H key, Collection hashKeys); + default Expirations getExpire(H key, Collection hashKeys) { + return getExpire(key, TimeUnit.SECONDS, hashKeys); + } /** * Get the time to live for {@code hashKey} (aka field) and convert it to the given {@link TimeUnit}. @@ -300,7 +306,8 @@ public interface HashOperations { * @since 3.5 */ @Nullable - List getExpire(H key, TimeUnit timeUnit, Collection hashKeys); + Expirations getExpire(H key, TimeUnit timeUnit, Collection hashKeys); + /** * @return never {@literal null}. */ diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java index 2151590ecc..2d0cbcaf10 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java @@ -15,13 +15,20 @@ */ package org.springframework.data.redis.core; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.core.types.Expiration; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.nio.ByteBuffer; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.springframework.lang.Nullable; /** * Reactive Redis operations for Hash Commands. @@ -230,10 +237,80 @@ default Flux> scan(H key) { */ Flux> scan(H key, ScanOptions options); + Mono> expire(H key, Duration timeout, Collection hashKeys); + + Mono> expire(H key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys); + + /** + * Set the expiration for given {@code hashKey} (aka field) as a {@literal date} timestamp. + * + * @param key must not be {@literal null}. + * @param expireAt must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. + * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable + Mono> expireAt(H key, Instant expireAt, Collection hashKeys); + + /** + * Remove the expiration from given {@code hashKey} (aka field). + * + * @param key must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is + * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HPERSIST + * @since 3.5 + */ + @Nullable + Mono> persist(H key, Collection hashKeys); + + /** + * Get the time to live for {@code hashKey} (aka field) in seconds. + * + * @param key must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative + * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration + * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + default Mono> getExpire(H key, Collection hashKeys) { + return getExpire(key, TimeUnit.SECONDS, hashKeys); + } + + /** + * Get the time to live for {@code hashKey} (aka field) and convert it to the given {@link TimeUnit}. + * + * @param key must not be {@literal null}. + * @param timeUnit must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative + * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration + * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + Mono> getExpire(H key, TimeUnit timeUnit, Collection hashKeys); + /** * Removes the given {@literal key}. * * @param key must not be {@literal null}. */ Mono delete(H key); + } diff --git a/src/main/java/org/springframework/data/redis/core/TimeoutUtils.java b/src/main/java/org/springframework/data/redis/core/TimeoutUtils.java index 86d90ca882..c46e8478d6 100644 --- a/src/main/java/org/springframework/data/redis/core/TimeoutUtils.java +++ b/src/main/java/org/springframework/data/redis/core/TimeoutUtils.java @@ -35,7 +35,11 @@ public abstract class TimeoutUtils { * @since 2.1 */ public static boolean hasMillis(Duration duration) { - return duration.toMillis() % 1000 != 0; + return containsSplitSecond(duration.toMillis()); + } + + public static boolean containsSplitSecond(long millis) { + return millis % 1000 != 0; } /** diff --git a/src/main/java/org/springframework/data/redis/core/types/Expiration.java b/src/main/java/org/springframework/data/redis/core/types/Expiration.java index 74eb9c3838..a68dd516b8 100644 --- a/src/main/java/org/springframework/data/redis/core/types/Expiration.java +++ b/src/main/java/org/springframework/data/redis/core/types/Expiration.java @@ -19,6 +19,7 @@ import java.util.Objects; import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.core.TimeoutUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -105,8 +106,8 @@ public static Expiration from(Duration duration) { Assert.notNull(duration, "Duration must not be null"); return duration.isZero() ? Expiration.persistent() - : duration.toMillis() % 1000 == 0 ? new Expiration(duration.getSeconds(), TimeUnit.SECONDS) - : new Expiration(duration.toMillis(), TimeUnit.MILLISECONDS); + : TimeoutUtils.hasMillis(duration) ? new Expiration(duration.toMillis(), TimeUnit.MILLISECONDS) + : new Expiration(duration.getSeconds(), TimeUnit.SECONDS); } /** diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java index 547c351875..ad22195ad0 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java @@ -20,13 +20,14 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.core.Expirations; +import org.springframework.data.redis.core.ExpireChanges; import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisOperations; @@ -327,27 +328,27 @@ public Cursor> scan() { } @Override - public List expire(Duration timeout, Collection hashKeys) { + public ExpireChanges expire(Duration timeout, Collection hashKeys) { return Objects.requireNonNull(hashOps.expire(timeout, hashKeys)); } @Override - public List expireAt(Instant expireAt, Collection hashKeys) { + public ExpireChanges expireAt(Instant expireAt, Collection hashKeys) { return Objects.requireNonNull(hashOps.expireAt(expireAt, hashKeys)); } @Override - public List persist(Collection hashKeys) { + public ExpireChanges persist(Collection hashKeys) { return Objects.requireNonNull(hashOps.persist(hashKeys)); } @Override - public List getExpire(Collection hashKeys) { + public Expirations getExpire(Collection hashKeys) { return Objects.requireNonNull(hashOps.getExpire(hashKeys)); } @Override - public List getExpire(TimeUnit timeUnit, Collection hashKeys) { + public Expirations getExpire(TimeUnit timeUnit, Collection hashKeys) { return Objects.requireNonNull(hashOps.getExpire(timeUnit, hashKeys)); } diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java index 4b79cf0290..54d002d549 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java @@ -19,11 +19,12 @@ import java.time.Instant; import java.util.Collection; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.core.Expirations; +import org.springframework.data.redis.core.ExpireChanges; import org.springframework.lang.Nullable; /** @@ -91,7 +92,7 @@ public interface RedisMap extends RedisStore, ConcurrentMap { * @see Redis Documentation: HEXPIRE * @since 3.5 */ - List expire(Duration timeout, Collection hashKeys); + ExpireChanges expire(Duration timeout, Collection hashKeys); /** * Set the expiration for given hash {@code key} as a {@literal date} timestamp. @@ -106,7 +107,7 @@ public interface RedisMap extends RedisStore, ConcurrentMap { * @see Redis Documentation: HEXPIRE * @since 3.5 */ - List expireAt(Instant expireAt, Collection hashKeys); + ExpireChanges expireAt(Instant expireAt, Collection hashKeys); /** * Remove the expiration from given hash {@code key}. @@ -118,7 +119,7 @@ public interface RedisMap extends RedisStore, ConcurrentMap { * @see Redis Documentation: HPERSIST * @since 3.5 */ - List persist(Collection hashKeys); + ExpireChanges persist(Collection hashKeys); /** * Get the time to live for hash {@code key} in seconds. @@ -130,7 +131,7 @@ public interface RedisMap extends RedisStore, ConcurrentMap { * @see Redis Documentation: HTTL * @since 3.5 */ - List getExpire(Collection hashKeys); + Expirations getExpire(Collection hashKeys); /** * Get the time to live for hash {@code key} and convert it to the given {@link TimeUnit}. @@ -143,5 +144,5 @@ public interface RedisMap extends RedisStore, ConcurrentMap { * @see Redis Documentation: HTTL * @since 3.5 */ - List getExpire(TimeUnit timeUnit, Collection hashKeys); + Expirations getExpire(TimeUnit timeUnit, Collection hashKeys); } diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java index 68981643d9..54d7f0c9d3 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java @@ -24,6 +24,8 @@ import java.util.concurrent.TimeUnit; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.core.Expirations; +import org.springframework.data.redis.core.ExpireChanges; import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.RedisOperations; import org.springframework.lang.Nullable; @@ -300,33 +302,38 @@ public Iterator> scan() { } @Override - public List expire(Duration timeout, Collection hashKeys) { + public ExpireChanges expire(Duration timeout, Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return Objects.requireNonNull(hashOps.expire(timeout, keys)); + return (ExpireChanges) hashOps.expire(timeout, keys); } @Override - public List expireAt(Instant expireAt, Collection hashKeys) { + public ExpireChanges expireAt(Instant expireAt, Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return Objects.requireNonNull(hashOps.expireAt(expireAt, keys)); + return (ExpireChanges) hashOps.expireAt(expireAt, keys); } @Override - public List persist(Collection hashKeys) { + public ExpireChanges persist(Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return Objects.requireNonNull(hashOps.persist(keys)); + return (ExpireChanges) hashOps.persist(keys); } @Override - public List getExpire(Collection hashKeys) { + public Expirations getExpire(Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return Objects.requireNonNull(hashOps.getExpire(keys)); + return (Expirations) hashOps.getExpire(keys); } @Override - public List getExpire(TimeUnit timeUnit, Collection hashKeys) { + public Expirations getExpire(TimeUnit timeUnit, Collection hashKeys) { + Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return Objects.requireNonNull(hashOps.getExpire(timeUnit, keys)); + return (Expirations) hashOps.getExpire(timeUnit, keys); } } diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index d42153cb68..ffa5bcd100 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -3436,6 +3436,7 @@ void hStrLenReturnsZeroWhenKeyDoesNotExist() { @Test @EnabledOnCommand("HEXPIRE") public void hExpireReturnsSuccessAndSetsTTL() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hExpire("hash-hexpire", 5L, "key-2")); actual.add(connection.hTtl("hash-hexpire", "key-2")); @@ -3449,6 +3450,7 @@ public void hExpireReturnsSuccessAndSetsTTL() { @Test @EnabledOnCommand("HEXPIRE") public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hExpire("hash-hexpire", 5L, "missking-field")); actual.add(connection.hExpire("missing-key", 5L, "key-2")); @@ -3459,6 +3461,7 @@ public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { @Test @EnabledOnCommand("HEXPIRE") public void hExpireReturnsTwoWhenZeroProvided() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hExpire("hash-hexpire", 0, "key-2")); @@ -3468,9 +3471,10 @@ public void hExpireReturnsTwoWhenZeroProvided() { @Test @EnabledOnCommand("HPEXPIRE") public void hpExpireReturnsSuccessAndSetsTTL() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hpExpire("hash-hexpire", 5000L, "key-2")); - actual.add(connection.hTtl("hash-hexpire", TimeUnit.MILLISECONDS,"key-2")); + actual.add(connection.hpTtl("hash-hexpire", "key-2")); List results = getResults(); assertThat(results.get(0)).isEqualTo(Boolean.TRUE); @@ -3481,6 +3485,7 @@ public void hpExpireReturnsSuccessAndSetsTTL() { @Test @EnabledOnCommand("HPEXPIRE") public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hpExpire("hash-hexpire", 5L, "missing-field")); actual.add(connection.hpExpire("missing-key", 5L, "key-2")); @@ -3491,6 +3496,7 @@ public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { @Test @EnabledOnCommand("HPEXPIRE") public void hpExpireReturnsTwoWhenZeroProvided() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hpExpire("hash-hexpire", 0, "key-2")); @@ -3500,6 +3506,7 @@ public void hpExpireReturnsTwoWhenZeroProvided() { @Test @EnabledOnCommand("HEXPIREAT") public void hExpireAtReturnsSuccessAndSetsTTL() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); @@ -3515,6 +3522,7 @@ public void hExpireAtReturnsSuccessAndSetsTTL() { @Test @EnabledOnCommand("HEXPIREAT") public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); @@ -3527,6 +3535,7 @@ public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { @Test @EnabledOnCommand("HEXPIREAT") public void hExpireAtReturnsTwoWhenZeroProvided() { + long fiveSecondsAgo = Instant.now().minusSeconds(5L).getEpochSecond(); actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); @@ -3538,6 +3547,7 @@ public void hExpireAtReturnsTwoWhenZeroProvided() { @Test @EnabledOnCommand("HEXPIREAT") public void hpExpireAtReturnsSuccessAndSetsTTL() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); @@ -3553,6 +3563,7 @@ public void hpExpireAtReturnsSuccessAndSetsTTL() { @Test @EnabledOnCommand("HEXPIREAT") public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); @@ -3565,6 +3576,7 @@ public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { @Test @EnabledOnCommand("HPEXPIREAT") public void hpExpireAdReturnsTwoWhenZeroProvided() { + long fiveSecondsAgo = Instant.now().minusSeconds(5L).getEpochSecond(); actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); @@ -3576,6 +3588,7 @@ public void hpExpireAdReturnsTwoWhenZeroProvided() { @Test @EnabledOnCommand("HPERSIST") public void hPersistReturnsSuccessAndPersistsField() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hExpire("hash-hexpire", 5L, "key-2")); actual.add(connection.hPersist("hash-hexpire", "key-2")); @@ -3587,6 +3600,7 @@ public void hPersistReturnsSuccessAndPersistsField() { @Test @EnabledOnCommand("HPERSIST") public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hPersist("hash-hexpire", "key-2")); @@ -3596,6 +3610,7 @@ public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { @Test @EnabledOnCommand("HPERSIST") public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hPersist("hash-hexpire", "missing-field")); actual.add(connection.hPersist("missing-key", "key-2")); @@ -3606,15 +3621,27 @@ public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { @Test @EnabledOnCommand("HTTL") public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hTtl("hash-hexpire", "key-2")); verifyResults(Arrays.asList(Boolean.TRUE, List.of(-1L))); } + @Test + @EnabledOnCommand("HTTL") + public void hTtlReturnsMinusIndependendOfTimeUnitOneWhenFieldHasNoExpiration() { + + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); + actual.add(connection.hTtl("hash-hexpire", TimeUnit.HOURS, "key-2")); + + verifyResults(Arrays.asList(Boolean.TRUE, List.of(-1L))); + } + @Test @EnabledOnCommand("HTTL") public void hTtlReturnsMinusTwoWhenFieldOrKeyMissing() { + actual.add(connection.hTtl("hash-hexpire", "missing-field")); actual.add(connection.hTtl("missing-key", "key-2")); diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 022d2e6a52..4e41e60954 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -1041,6 +1041,7 @@ public void hStrLenReturnsZeroWhenKeyDoesNotExist() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireReturnsSuccessAndSetsTTL() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1050,6 +1051,7 @@ public void hExpireReturnsSuccessAndSetsTTL() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); // missing field @@ -1059,6 +1061,7 @@ public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireReturnsTwoWhenZeroProvided() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1066,6 +1069,7 @@ public void hExpireReturnsTwoWhenZeroProvided() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsSuccessAndSetsTTL() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1075,6 +1079,7 @@ public void hpExpireReturnsSuccessAndSetsTTL() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); // missing field @@ -1084,6 +1089,7 @@ public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsTwoWhenZeroProvided() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1091,6 +1097,7 @@ public void hpExpireReturnsTwoWhenZeroProvided() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireAtReturnsSuccessAndSetsTTL() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); @@ -1099,6 +1106,7 @@ public void hExpireAtReturnsSuccessAndSetsTTL() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); @@ -1110,6 +1118,7 @@ public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireAdReturnsTwoWhenZeroProvided() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1117,6 +1126,7 @@ public void hExpireAdReturnsTwoWhenZeroProvided() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireAtReturnsSuccessAndSetsTTL() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); @@ -1126,6 +1136,7 @@ public void hpExpireAtReturnsSuccessAndSetsTTL() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); @@ -1137,6 +1148,7 @@ public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireAdReturnsTwoWhenZeroProvided() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1144,6 +1156,7 @@ public void hpExpireAdReturnsTwoWhenZeroProvided() { } @Test + @EnabledOnCommand("HEXPIRE") public void hPersistReturnsSuccessAndPersistsField() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); @@ -1152,12 +1165,14 @@ public void hPersistReturnsSuccessAndPersistsField() { } @Test + @EnabledOnCommand("HEXPIRE") public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); } @Test + @EnabledOnCommand("HEXPIRE") public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1167,14 +1182,15 @@ public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { } @Test + @EnabledOnCommand("HEXPIRE") public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); - } @Test + @EnabledOnCommand("HEXPIRE") public void hTtlReturnsMinusTwoWhenFieldOrKeyMissing() { assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java index c779322576..5611fb351f 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java @@ -1098,7 +1098,9 @@ public void hStrLenReturnsZeroWhenKeyDoesNotExist() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); @@ -1106,7 +1108,9 @@ public void hExpireReturnsSuccessAndSetsTTL() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); // missing field assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_1_BYTES)).contains(-2L); @@ -1115,6 +1119,7 @@ public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireReturnsTwoWhenZeroProvided() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1122,16 +1127,19 @@ public void hExpireReturnsTwoWhenZeroProvided() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsSuccessAndSetsTTL() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 5000L, KEY_2_BYTES)).contains(1L); - assertThat(clusterConnection.hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS,KEY_2_BYTES)) + assertThat(clusterConnection.hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS, KEY_2_BYTES)) .allSatisfy(val -> assertThat(val).isBetween(0L, 5000L)); } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); // missing field assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 5L, KEY_1_BYTES)).contains(-2L); @@ -1140,14 +1148,18 @@ public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireAtReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); @@ -1155,17 +1167,21 @@ public void hExpireAtReturnsSuccessAndSetsTTL() { } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); // missing field assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_1_BYTES)).contains(-2L); + // missing key assertThat(clusterConnection.hashCommands().hExpireAt(KEY_2_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(-2L); } @Test + @EnabledOnCommand("HEXPIRE") public void hExpireAdReturnsTwoWhenZeroProvided() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1173,16 +1189,20 @@ public void hExpireAdReturnsTwoWhenZeroProvided() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireAtReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); - assertThat(clusterConnection.hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS, KEY_2_BYTES)) - .allSatisfy(val -> assertThat(val).isBetween(0L, 5000L)); + assertThat(clusterConnection.hpTtl(KEY_1_BYTES, KEY_2_BYTES)) + .allSatisfy(val -> assertThat(val).isGreaterThan(1000L).isLessThanOrEqualTo(5000L)); } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); @@ -1193,14 +1213,18 @@ public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { } @Test + @EnabledOnCommand("HEXPIRE") public void hpExpireAdReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); } @Test + @EnabledOnCommand("HEXPIRE") public void hPersistReturnsSuccessAndPersistsField() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(1L); @@ -1208,12 +1232,15 @@ public void hPersistReturnsSuccessAndPersistsField() { } @Test + @EnabledOnCommand("HEXPIRE") public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); } @Test + @EnabledOnCommand("HEXPIRE") public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); @@ -1223,19 +1250,21 @@ public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { } @Test + @EnabledOnCommand("HEXPIRE") public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); - + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, TimeUnit.HOURS, KEY_2_BYTES)).contains(-1L); } @Test + @EnabledOnCommand("HEXPIRE") public void hTtlReturnsMinusTwoWhenFieldOrKeyMissing() { assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); assertThat(clusterConnection.hashCommands().hTtl(KEY_3_BYTES,KEY_2_BYTES)).contains(-2L); - } @Test // DATAREDIS-315 diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java index 86b3ca74f1..4ef5fcffe3 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.*; +import org.springframework.data.redis.test.condition.EnabledOnCommand; import reactor.test.StepVerifier; import java.nio.ByteBuffer; @@ -293,6 +294,7 @@ void hStrLenReturnsZeroWhenKeyDoesNotExist() { } @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void hExpireShouldHandleMultipleParametersCorrectly() { assertThat(nativeCommands.hset(KEY_1, FIELD_1, VALUE_1)).isTrue(); assertThat(nativeCommands.hset(KEY_1, FIELD_2, VALUE_2)).isTrue(); @@ -312,6 +314,7 @@ void hExpireShouldHandleMultipleParametersCorrectly() { } @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void hExpireAtShouldHandleMultipleParametersCorrectly() { assertThat(nativeCommands.hset(KEY_1, FIELD_1, VALUE_1)).isTrue(); assertThat(nativeCommands.hset(KEY_1, FIELD_2, VALUE_2)).isTrue(); @@ -330,6 +333,7 @@ void hExpireAtShouldHandleMultipleParametersCorrectly() { } @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void hPersistShouldPersistFields() { assertThat(nativeCommands.hset(KEY_1, FIELD_1, VALUE_1)).isTrue(); assertThat(nativeCommands.hset(KEY_1, FIELD_2, VALUE_2)).isTrue(); diff --git a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java index ef9737d7a4..4abd23dacf 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java @@ -15,8 +15,9 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assumptions.assumeThat; import java.io.IOException; import java.time.Duration; @@ -27,13 +28,18 @@ import java.util.Map; import java.util.concurrent.TimeUnit; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; - +import org.junit.jupiter.api.Test; import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.StringObjectFactory; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension; +import org.springframework.data.redis.core.Expirations.Expiration; +import org.springframework.data.redis.core.ExpireChanges.ExpiryChangeState; +import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.extension.RedisStanalone; import org.springframework.data.redis.test.extension.parametrized.MethodSource; import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest; @@ -136,7 +142,6 @@ void testHScanReadsValuesFully() throws IOException { hashOps.put(key, key1, val1); hashOps.put(key, key2, val2); - long count = 0; try (Cursor> it = hashOps.scan(key, ScanOptions.scanOptions().count(1).build())) { @@ -208,6 +213,7 @@ void randomValue() { assertThat(values).hasSize(2).containsEntry(key1, val1).containsEntry(key2, val2); } + @EnabledOnCommand("HEXPIRE") @ParameterizedRedisTest void testExpireAndGetExpireMillis() { @@ -220,13 +226,20 @@ void testExpireAndGetExpireMillis() { hashOps.put(key, key2, val2); assertThat(redisTemplate.opsForHash().expire(key, Duration.ofMillis(500), List.of(key1))) - .containsExactly(1L); + .satisfies(ExpireChanges::allOk); + + assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1))).satisfies(expirations -> { - assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1))) - .allSatisfy(it -> assertThat(it).isBetween(0L, 500L)); + assertThat(expirations.missing()).isEmpty(); + assertThat(expirations.precision()).isEqualTo(TimeUnit.SECONDS); + assertThat(expirations.expirationOf(key1)).extracting(Expiration::raw, InstanceOfAssertFactories.LONG) + .isBetween(0L, 1L); + assertThat(expirations.ttlOf(key1)).isBetween(Duration.ZERO, Duration.ofSeconds(1)); + }); } @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void testExpireAndGetExpireSeconds() { K key = keyFactory.instance(); @@ -238,13 +251,26 @@ void testExpireAndGetExpireSeconds() { hashOps.put(key, key2, val2); assertThat(redisTemplate.opsForHash().expire(key, Duration.ofSeconds(5), List.of(key1, key2))) - .containsExactly(1L, 1L); + .satisfies(changes -> { + assertThat(changes.allOk()).isTrue(); + assertThat(changes.stateOf(key1)).isEqualTo(ExpiryChangeState.OK); + assertThat(changes.ok()).containsExactlyInAnyOrder(key1, key2); + assertThat(changes.missed()).isEmpty(); + assertThat(changes.stateChanges()).map(ExpiryChangeState::value).containsExactly(1L, 1L); + }); assertThat(redisTemplate.opsForHash().getExpire(key, TimeUnit.SECONDS, List.of(key1, key2))) - .allSatisfy(it -> assertThat(it).isBetween(0L, 5L)); + .satisfies(expirations -> { + assertThat(expirations.missing()).isEmpty(); + assertThat(expirations.precision()).isEqualTo(TimeUnit.SECONDS); + assertThat(expirations.expirationOf(key1)).extracting(Expiration::raw, InstanceOfAssertFactories.LONG) + .isBetween(0L, 5L); + assertThat(expirations.ttlOf(key1)).isBetween(Duration.ofSeconds(1), Duration.ofSeconds(5)); + }); } @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void testExpireAtAndGetExpireMillis() { K key = keyFactory.instance(); @@ -256,13 +282,69 @@ void testExpireAtAndGetExpireMillis() { hashOps.put(key, key2, val2); assertThat(redisTemplate.opsForHash().expireAt(key, Instant.now().plusMillis(500), List.of(key1, key2))) - .containsExactly(1L, 1L); + .satisfies(ExpireChanges::allOk); + + assertThat(redisTemplate.opsForHash().getExpire(key, TimeUnit.MILLISECONDS, List.of(key1, key2))) + .satisfies(expirations -> { + assertThat(expirations.missing()).isEmpty(); + assertThat(expirations.precision()).isEqualTo(TimeUnit.MILLISECONDS); + assertThat(expirations.expirationOf(key1)).extracting(Expiration::raw, InstanceOfAssertFactories.LONG) + .isBetween(0L, 500L); + assertThat(expirations.ttlOf(key1)).isBetween(Duration.ZERO, Duration.ofMillis(500)); + }); + } - assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1, key2))) - .allSatisfy(it -> assertThat(it).isBetween(0L, 500L)); + @ParameterizedRedisTest + void expireThrowsErrorOfNanoPrecision() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> redisTemplate.opsForHash().getExpire(key, TimeUnit.NANOSECONDS, List.of(key1))); } @ParameterizedRedisTest + void testExpireWithOptionsNone() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + + hashOps.put(key, key1, val1); + hashOps.put(key, key2, val2); + + ExpireChanges expire = redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), List.of(key1)); + + assertThat(expire.allOk()).isTrue(); + } + + @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") + void testExpireWithOptions() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + + hashOps.put(key, key1, val1); + hashOps.put(key, key2, val2); + + redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), List.of(key1)); + redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(60), FieldExpirationOptions.none(), List.of(key2)); + + ExpireChanges changes = redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(30), FieldExpirationOptions.builder().gt().build(), List.of(key1, key2)); + + assertThat(changes.ok()).containsExactly(key1); + assertThat(changes.skipped()).containsExactly(key2); + } + + @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void testPersistAndGetExpireMillis() { K key = keyFactory.instance(); @@ -273,13 +355,14 @@ void testPersistAndGetExpireMillis() { hashOps.put(key, key1, val1); hashOps.put(key, key2, val2); - assertThat(redisTemplate.opsForHash().expireAt(key, Instant.now().plusMillis(500), List.of(key1, key2))) - .containsExactly(1L, 1L); + assertThat(redisTemplate.opsForHash().expireAt(key, Instant.now().plusMillis(800), List.of(key1, key2))) + .satisfies(ExpireChanges::allOk); - assertThat(redisTemplate.opsForHash().persist(key, List.of(key1, key2))) - .allSatisfy(it -> assertThat(it).isEqualTo(1L)); + assertThat(redisTemplate.opsForHash().persist(key, List.of(key2))).satisfies(ExpireChanges::allOk); - assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1, key2))) - .allSatisfy(it -> assertThat(it).isEqualTo(-1L)); + assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1, key2))).satisfies(expirations -> { + assertThat(expirations.expirationOf(key1).isPersistent()).isFalse(); + assertThat(expirations.expirationOf(key2).isPersistent()).isTrue(); + }); } } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java index dd1e1287ef..a128a29293 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java @@ -15,28 +15,32 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; -import static org.junit.jupiter.api.condition.OS.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.junit.jupiter.api.condition.OS.MAC; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.springframework.data.redis.connection.convert.Converters; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import reactor.test.StepVerifier; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; - +import org.junit.jupiter.api.condition.DisabledOnOs; import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.SettingsUtils; import org.springframework.data.redis.StringObjectFactory; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; @@ -501,6 +505,141 @@ void scan() { .verifyComplete(); } + @EnabledOnCommand("HEXPIRE") + @ParameterizedRedisTest + void testExpireAndGetExpireMillis() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + + putAll(key, key1, val1, key2, val2); + + hashOperations.expire(key, Duration.ofMillis(1500), List.of(key1)) // + .as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.allOk()).isTrue(); + }).verifyComplete(); + + hashOperations.getExpire(key, List.of(key1)) // + .as(StepVerifier::create) // + .assertNext(it -> { + assertThat(it.expirationOf(key1).raw()).isBetween(0L, 2L); + }).verifyComplete(); + } + + @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") + void testExpireWithOptions() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + + putAll(key, key1, val1, key2, val2); + + hashOperations.expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), List.of(key1)).as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.allOk()).isTrue(); + }).verifyComplete(); + hashOperations.expire(key, org.springframework.data.redis.core.types.Expiration.seconds(60), FieldExpirationOptions.none(), List.of(key2)).as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.allOk()).isTrue(); + }).verifyComplete(); + + hashOperations.expire(key, org.springframework.data.redis.core.types.Expiration.seconds(30), FieldExpirationOptions.builder().gt().build(), List.of(key1, key2)).as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.ok()).containsExactly(key1); + assertThat(changes.skipped()).containsExactly(key2); + }).verifyComplete(); + } + + @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") + void testExpireAndGetExpireSeconds() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + + putAll(key, key1, val1, key2, val2); + + hashOperations.expire(key, Duration.ofSeconds(5), List.of(key1, key2)) // + .as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.allOk()).isTrue(); + }).verifyComplete(); + + hashOperations.getExpire(key, TimeUnit.SECONDS, List.of(key1, key2)) // + .as(StepVerifier::create) // + .assertNext(it -> { + assertThat(it.expirationOf(key1).raw()).isBetween(0L, 5L); + assertThat(it.expirationOf(key2).raw()).isBetween(0L, 5L); + }).verifyComplete(); + + } + + @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") + void testExpireAtAndGetExpireMillis() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + + putAll(key, key1, val1, key2, val2); + + redisTemplate.opsForHash().expireAt(key, Instant.now().plusMillis(1500), List.of(key1, key2)) + .as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.allOk()).isTrue(); + }).verifyComplete(); + + redisTemplate.opsForHash().getExpire(key, List.of(key1, key2)).as(StepVerifier::create)// + .assertNext(it -> { + assertThat(it.expirationOf(key1).raw()).isBetween(0L, 2L); + assertThat(it.expirationOf(key2).raw()).isBetween(0L, 2L); + }).verifyComplete(); + } + + @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") + void testPersistAndGetExpireMillis() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + + putAll(key, key1, val1, key2, val2); + + redisTemplate.opsForHash().expireAt(key, Instant.now().plusMillis(1500), List.of(key1, key2)) + .as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.allOk()).isTrue(); + }).verifyComplete(); + + redisTemplate.opsForHash().persist(key, List.of(key1, key2)).as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.allOk()).isTrue(); + }).verifyComplete(); + + redisTemplate.opsForHash().getExpire(key, List.of(key1, key2)).as(StepVerifier::create)// + .assertNext(expirations -> { + assertThat(expirations.persistent()).contains(key1, key2); + }).verifyComplete(); + + } + @ParameterizedRedisTest // DATAREDIS-602 void delete() { diff --git a/src/test/java/org/springframework/data/redis/core/ExpirationsUnitTest.java b/src/test/java/org/springframework/data/redis/core/ExpirationsUnitTest.java new file mode 100644 index 0000000000..5fc1953d3d --- /dev/null +++ b/src/test/java/org/springframework/data/redis/core/ExpirationsUnitTest.java @@ -0,0 +1,94 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.core; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.springframework.data.redis.core.Expirations.Timeouts; + +/** + * @author Christoph Strobl + * @since 2025/02 + */ +class ExpirationsUnitTest { + + static final String KEY_1 = "key-1"; + static final String KEY_2 = "key-2"; + static final String KEY_3 = "key-3"; + + @ParameterizedTest + @EnumSource(TimeUnit.class) + void expirationMemorizesSourceUnit(TimeUnit targetUnit) { + + Expirations exp = Expirations.of(targetUnit, List.of(KEY_1), new Timeouts(TimeUnit.SECONDS, List.of(120L))); + + assertThat(exp.expirations().get(0)).satisfies(expiration -> { + assertThat(expiration.raw()).isEqualTo(120L); + assertThat(expiration.value()).isEqualTo(targetUnit.convert(120, TimeUnit.SECONDS)); + }); + } + + @Test + void expirationsCategorizesElements() { + + Expirations exp = createExpirations(new Timeouts(TimeUnit.SECONDS, List.of(-2L, -1L, 120L))); + + assertThat(exp.persistent()).containsExactly(KEY_2); + assertThat(exp.missing()).containsExactly(KEY_1); + assertThat(exp.expiring()).containsExactly(Map.entry(KEY_3, Duration.ofMinutes(2))); + } + + @Test + void returnsNullForMissingElements() { + + Expirations exp = createExpirations(new Timeouts(TimeUnit.SECONDS, List.of(-2L, -1L, 120L))); + + assertThat(exp.expirationOf("missing")).isNull(); + assertThat(exp.ttlOf("missing")).isNull(); + } + + @Test + void ttlReturnsDurationForEntriesWithTimeout() { + + Expirations exp = createExpirations(new Timeouts(TimeUnit.SECONDS, List.of(-2L, -1L, 120L))); + + assertThat(exp.ttlOf(KEY_3)).isEqualTo(Duration.ofMinutes(2)); + } + + @Test + void ttlReturnsNullForPersistentAndMissingEntries() { + + Expirations exp = createExpirations(new Timeouts(TimeUnit.SECONDS, List.of(-2L, -1L, 120L))); + + assertThat(exp.ttlOf(KEY_1)).isNull(); + assertThat(exp.ttlOf(KEY_2)).isNull(); + } + + static Expirations createExpirations(Timeouts timeouts) { + + List keys = IntStream.range(1, timeouts.raw().size() + 1).mapToObj("key-%s"::formatted).toList(); + return Expirations.of(timeouts.timeUnit(), keys, timeouts); + } +} diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java index 0f364c45c6..5ce8e54418 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java @@ -15,8 +15,9 @@ */ package org.springframework.data.redis.support.collections; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assumptions.assumeThat; import java.io.IOException; import java.text.DecimalFormat; @@ -41,6 +42,7 @@ import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.RedisSystemException; +import org.springframework.data.redis.core.ExpireChanges; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisOperations; @@ -195,31 +197,41 @@ void testIncrement() { } @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void testExpire() { + K k1 = getKey(); V v1 = getValue(); assertThat(map.put(k1, v1)).isEqualTo(null); Collection keys = Collections.singletonList(k1); - assertThat(map.expire(Duration.ofSeconds(5), keys)).contains(1L); - assertThat(map.getExpire(keys)).allSatisfy(expiration -> assertThat(expiration).isBetween(1L, 5L)); - assertThat(map.getExpire(TimeUnit.MILLISECONDS, keys)) - .allSatisfy(expiration -> assertThat(expiration).isBetween(1000L, 5000L)); - assertThat(map.persist(keys)).contains(1L); + assertThat(map.expire(Duration.ofSeconds(5), keys)).satisfies(ExpireChanges::allOk); + assertThat(map.getExpire(keys)).satisfies(expiration -> { + assertThat(expiration.expirationOf(k1).raw()).isBetween(1L, 5L); + }); + assertThat(map.getExpire(TimeUnit.MILLISECONDS, keys)).satisfies(expiration -> { + assertThat(expiration.expirationOf(k1).raw()).isBetween(1000L, 5000L); + }); + assertThat(map.persist(keys)).satisfies(ExpireChanges::allOk); } @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void testExpireAt() { + K k1 = getKey(); V v1 = getValue(); assertThat(map.put(k1, v1)).isEqualTo(null); Collection keys = Collections.singletonList(k1); - assertThat(map.expireAt(Instant.now().plusSeconds(5), keys)).contains(1L); - assertThat(map.getExpire(keys)).allSatisfy(expiration -> assertThat(expiration).isBetween(1L, 5L)); - assertThat(map.getExpire(TimeUnit.MILLISECONDS, keys)) - .allSatisfy(expiration -> assertThat(expiration).isBetween(1000L, 5000L)); - assertThat(map.persist(keys)).contains(1L); + assertThat(map.expireAt(Instant.now().plusSeconds(5), keys)).satisfies(ExpireChanges::allOk); + assertThat(map.getExpire(keys)).satisfies(expiration -> { + assertThat(expiration.expirationOf(k1).raw()).isBetween(1L, 5L); + }); + assertThat(map.getExpire(TimeUnit.MILLISECONDS, keys)).satisfies(expiration -> { + assertThat(expiration.expirationOf(k1).raw()).isBetween(1000L, 5000L); + }); + assertThat(map.persist(keys)).satisfies(ExpireChanges::allOk); } @ParameterizedRedisTest From c6ba95f27143f3e896ac848222966f435c715d8e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 11 Feb 2025 11:32:38 +0100 Subject: [PATCH 139/187] Polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce command variants with Condition to avoid duplicate expireHashField(…) implementations. Move expireHashField to default method and rename it to applyExpiration(…). Rename Expiration to TimeToLive and methods to getTimeToLive(…). Fix since tags. Extract BoundHashFieldExpirationOperations to provide a fluent way of interacting with expirations on BoundHashOps and RedisMap. Add Hash Field Expiration commands to TypeHints for Lettuce. See #3054 --- .../antora/modules/ROOT/pages/appendix.adoc | 374 +++++++++--------- .../DefaultStringRedisConnection.java | 57 +-- .../connection/DefaultedRedisConnection.java | 52 ++- .../data/redis/connection/Hash.java | 56 ++- .../connection/ReactiveHashCommands.java | 25 +- .../redis/connection/RedisHashCommands.java | 244 +++++++++--- .../connection/StringRedisConnection.java | 174 +++++--- .../jedis/JedisClusterHashCommands.java | 83 ++-- .../connection/jedis/JedisHashCommands.java | 69 ++-- .../connection/lettuce/LettuceConnection.java | 8 + .../lettuce/LettuceHashCommands.java | 74 ++-- .../lettuce/LettuceReactiveHashCommands.java | 6 +- .../BoundHashFieldExpirationOperations.java | 126 ++++++ .../data/redis/core/BoundHashOperations.java | 131 ++---- ...ultBoundHashFieldExpirationOperations.java | 93 +++++ .../redis/core/DefaultHashOperations.java | 24 +- .../core/DefaultReactiveHashOperations.java | 18 +- .../data/redis/core/ExpireChanges.java | 22 +- .../data/redis/core/HashOperations.java | 120 ++++-- .../redis/core/ReactiveHashOperations.java | 29 +- .../data/redis/core/RedisCommand.java | 7 + .../data/redis/core/RedisOperations.java | 1 + .../redis/core/{ => types}/Expirations.java | 180 +++++---- .../support/collections/DefaultRedisMap.java | 29 +- .../redis/support/collections/RedisMap.java | 77 +--- .../support/collections/RedisProperties.java | 50 +-- .../AbstractConnectionIntegrationTests.java | 42 +- ...DefaultHashOperationsIntegrationTests.java | 65 ++- ...eactiveHashOperationsIntegrationTests.java | 17 +- .../core/{ => types}/ExpirationsUnitTest.java | 31 +- .../AbstractRedisMapIntegrationTests.java | 29 +- .../RedisPropertiesIntegrationTests.java | 6 - 32 files changed, 1426 insertions(+), 893 deletions(-) create mode 100644 src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java create mode 100644 src/main/java/org/springframework/data/redis/core/DefaultBoundHashFieldExpirationOperations.java rename src/main/java/org/springframework/data/redis/core/{ => types}/Expirations.java (62%) rename src/test/java/org/springframework/data/redis/core/{ => types}/ExpirationsUnitTest.java (80%) diff --git a/src/main/antora/modules/ROOT/pages/appendix.adoc b/src/main/antora/modules/ROOT/pages/appendix.adoc index 46feff0611..669bf82204 100644 --- a/src/main/antora/modules/ROOT/pages/appendix.adoc +++ b/src/main/antora/modules/ROOT/pages/appendix.adoc @@ -8,193 +8,201 @@ link:https://www.springframework.org/schema/redis/spring-redis-1.0.xsd[Spring Da [[supported-commands]] == Supported Commands + .Redis commands supported by `RedisTemplate` [width="50%",cols="<2,^1",options="header"] |========================================================= |Command |Template Support -|APPEND |X -|AUTH |X -|BGREWRITEAOF |X -|BGSAVE |X -|BITCOUNT |X -|BITFIELD |X -|BITOP |X -|BLPOP |X -|BRPOP |X -|BRPOPLPUSH |X -|CLIENT KILL |X -|CLIENT GETNAME |X -|CLIENT LIST |X -|CLIENT SETNAME |X -|CLUSTER SLOTS |- -|COMMAND |- -|COMMAND COUNT |- -|COMMAND GETKEYS |- -|COMMAND INFO |- -|CONFIG GET |X -|CONFIG RESETSTAT |X -|CONFIG REWRITE |- -|CONFIG SET |X -|DBSIZE |X -|DEBUG OBJECT |- -|DEBUG SEGFAULT |- -|DECR |X -|DECRBY |X -|DEL |X -|DISCARD |X -|DUMP |X -|ECHO |X -|EVAL |X -|EVALSHA |X -|EXEC |X -|EXISTS |X -|EXPIRE |X -|EXPIREAT |X -|FLUSHALL |X -|FLUSHDB |X -|GEOADD |X -|GEODIST |X -|GEOHASH |X -|GEOPOS |X -|GEORADIUS |X -|GEORADIUSBYMEMBER |X -|GEOSEARCH |X -|GEOSEARCHSTORE |X -|GET |X -|GETBIT |X -|GETRANGE |X -|GETSET |X -|HDEL |X -|HEXISTS |X -|HGET |X -|HGETALL |X -|HINCRBY |X -|HINCRBYFLOAT |X -|HKEYS |X -|HLEN |X -|HMGET |X -|HMSET |X -|HSCAN |X -|HSET |X -|HSETNX |X -|HVALS |X -|INCR |X -|INCRBY |X -|INCRBYFLOAT |X -|INFO |X -|KEYS |X -|LASTSAVE |X -|LINDEX |X -|LINSERT |X -|LLEN |X -|LPOP |X -|LPUSH |X -|LPUSHX |X -|LRANGE |X -|LREM |X -|LSET |X -|LTRIM |X -|MGET |X -|MIGRATE |- -|MONITOR |- -|MOVE |X -|MSET |X -|MSETNX |X -|MULTI |X -|OBJECT |- -|PERSIST |X -|PEXIPRE |X -|PEXPIREAT |X -|PFADD |X -|PFCOUNT |X -|PFMERGE |X -|PING |X -|PSETEX |X -|PSUBSCRIBE |X -|PTTL |X -|PUBLISH |X -|PUBSUB |- -|PUBSUBSCRIBE |- -|QUIT |X -|RANDOMKEY |X -|RENAME |X -|RENAMENX |X -|REPLICAOF |X -|RESTORE |X -|ROLE |- -|RPOP |X -|RPOPLPUSH |X -|RPUSH |X -|RPUSHX |X -|SADD |X -|SAVE |X -|SCAN |X -|SCARD |X -|SCRIPT EXITS |X -|SCRIPT FLUSH |X -|SCRIPT KILL |X -|SCRIPT LOAD |X -|SDIFF |X -|SDIFFSTORE |X -|SELECT |X -|SENTINEL FAILOVER |X +|APPEND |X +|AUTH |X +|BGREWRITEAOF |X +|BGSAVE |X +|BITCOUNT |X +|BITFIELD |X +|BITOP |X +|BLPOP |X +|BRPOP |X +|BRPOPLPUSH |X +|CLIENT KILL |X +|CLIENT GETNAME |X +|CLIENT LIST |X +|CLIENT SETNAME |X +|CLUSTER SLOTS |- +|COMMAND |- +|COMMAND COUNT |- +|COMMAND GETKEYS |- +|COMMAND INFO |- +|CONFIG GET |X +|CONFIG RESETSTAT |X +|CONFIG REWRITE |- +|CONFIG SET |X +|DBSIZE |X +|DEBUG OBJECT |- +|DEBUG SEGFAULT |- +|DECR |X +|DECRBY |X +|DEL |X +|DISCARD |X +|DUMP |X +|ECHO |X +|EVAL |X +|EVALSHA |X +|EXEC |X +|EXISTS |X +|EXPIRE |X +|EXPIREAT |X +|FLUSHALL |X +|FLUSHDB |X +|GEOADD |X +|GEODIST |X +|GEOHASH |X +|GEOPOS |X +|GEORADIUS |X +|GEORADIUSBYMEMBER |X +|GEOSEARCH |X +|GEOSEARCHSTORE |X +|GET |X +|GETBIT |X +|GETRANGE |X +|GETSET |X +|HDEL |X +|HEXISTS |X +|HEXPIRE |X +|HEXPIREAT |X +|HPEXPIRE |X +|HPEXPIREAT |X +|HPERSIST |X +|HTTL |X +|HPTTL |X +|HGET |X +|HGETALL |X +|HINCRBY |X +|HINCRBYFLOAT |X +|HKEYS |X +|HLEN |X +|HMGET |X +|HMSET |X +|HSCAN |X +|HSET |X +|HSETNX |X +|HVALS |X +|INCR |X +|INCRBY |X +|INCRBYFLOAT |X +|INFO |X +|KEYS |X +|LASTSAVE |X +|LINDEX |X +|LINSERT |X +|LLEN |X +|LPOP |X +|LPUSH |X +|LPUSHX |X +|LRANGE |X +|LREM |X +|LSET |X +|LTRIM |X +|MGET |X +|MIGRATE |- +|MONITOR |- +|MOVE |X +|MSET |X +|MSETNX |X +|MULTI |X +|OBJECT |- +|PERSIST |X +|PEXIPRE |X +|PEXPIREAT |X +|PFADD |X +|PFCOUNT |X +|PFMERGE |X +|PING |X +|PSETEX |X +|PSUBSCRIBE |X +|PTTL |X +|PUBLISH |X +|PUBSUB |- +|PUBSUBSCRIBE |- +|QUIT |X +|RANDOMKEY |X +|RENAME |X +|RENAMENX |X +|REPLICAOF |X +|RESTORE |X +|ROLE |- +|RPOP |X +|RPOPLPUSH |X +|RPUSH |X +|RPUSHX |X +|SADD |X +|SAVE |X +|SCAN |X +|SCARD |X +|SCRIPT EXITS |X +|SCRIPT FLUSH |X +|SCRIPT KILL |X +|SCRIPT LOAD |X +|SDIFF |X +|SDIFFSTORE |X +|SELECT |X +|SENTINEL FAILOVER |X |SENTINEL GET-MASTER-ADD-BY-NAME |- -|SENTINEL MASTER | - -|SENTINEL MASTERS |X -|SENTINEL MONITOR |X -|SENTINEL REMOVE |X -|SENTINEL RESET |- -|SENTINEL SET |- -|SENTINEL SLAVES |X -|SET |X -|SETBIT |X -|SETEX |X -|SETNX |X -|SETRANGE |X -|SHUTDOWN |X -|SINTER |X -|SINTERSTORE |X -|SISMEMBER |X -|SLAVEOF |X -|SLOWLOG |- -|SMEMBERS |X -|SMOVE |X -|SORT |X -|SPOP |X -|SRANDMEMBER |X -|SREM |X -|SSCAN |X -|STRLEN |X -|SUBSCRIBE |X -|SUNION |X -|SUNIONSTORE |X -|SYNC |- -|TIME |X -|TTL |X -|TYPE |X -|UNSUBSCRIBE |X -|UNWATCH |X -|WATCH |X -|ZADD |X -|ZCARD |X -|ZCOUNT |X -|ZINCRBY |X -|ZINTERSTORE |X -|ZLEXCOUNT |- -|ZRANGE |X -|ZRANGEBYLEX |- -|ZREVRANGEBYLEX |- -|ZRANGEBYSCORE |X -|ZRANGESTORE |X -|ZRANK |X -|ZREM |X -|ZREMRANGEBYLEX |- -|ZREMRANGEBYRANK |X -|ZREVRANGE |X -|ZREVRANGEBYSCORE |X -|ZREVRANK |X -|ZSCAN |X -|ZSCORE |X -|ZUNINONSTORE |X +|SENTINEL MASTER | - +|SENTINEL MASTERS |X +|SENTINEL MONITOR |X +|SENTINEL REMOVE |X +|SENTINEL RESET |- +|SENTINEL SET |- +|SENTINEL SLAVES |X +|SET |X +|SETBIT |X +|SETEX |X +|SETNX |X +|SETRANGE |X +|SHUTDOWN |X +|SINTER |X +|SINTERSTORE |X +|SISMEMBER |X +|SLAVEOF |X +|SLOWLOG |- +|SMEMBERS |X +|SMOVE |X +|SORT |X +|SPOP |X +|SRANDMEMBER |X +|SREM |X +|SSCAN |X +|STRLEN |X +|SUBSCRIBE |X +|SUNION |X +|SUNIONSTORE |X +|SYNC |- +|TIME |X +|TTL |X +|TYPE |X +|UNSUBSCRIBE |X +|UNWATCH |X +|WATCH |X +|ZADD |X +|ZCARD |X +|ZCOUNT |X +|ZINCRBY |X +|ZINTERSTORE |X +|ZLEXCOUNT |- +|ZRANGE |X +|ZRANGEBYLEX |- +|ZREVRANGEBYLEX |- +|ZRANGEBYSCORE |X +|ZRANGESTORE |X +|ZRANK |X +|ZREM |X +|ZREMRANGEBYLEX |- +|ZREMRANGEBYRANK |X +|ZREVRANGE |X +|ZREVRANGEBYSCORE |X +|ZREVRANK |X +|ZSCAN |X +|ZSCORE |X +|ZUNINONSTORE |X |========================================================= diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java index c3f13ef4a5..831a46ece2 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.convert.converter.Converter; import org.springframework.data.geo.Circle; import org.springframework.data.geo.Distance; @@ -35,10 +36,19 @@ import org.springframework.data.redis.connection.convert.ListConverter; import org.springframework.data.redis.connection.convert.MapConverter; import org.springframework.data.redis.connection.convert.SetConverter; -import org.springframework.data.redis.connection.stream.*; +import org.springframework.data.redis.connection.stream.ByteRecord; +import org.springframework.data.redis.connection.stream.Consumer; +import org.springframework.data.redis.connection.stream.MapRecord; +import org.springframework.data.redis.connection.stream.PendingMessages; +import org.springframework.data.redis.connection.stream.PendingMessagesSummary; +import org.springframework.data.redis.connection.stream.ReadOffset; +import org.springframework.data.redis.connection.stream.RecordId; import org.springframework.data.redis.connection.stream.StreamInfo.XInfoConsumers; import org.springframework.data.redis.connection.stream.StreamInfo.XInfoGroups; import org.springframework.data.redis.connection.stream.StreamInfo.XInfoStream; +import org.springframework.data.redis.connection.stream.StreamOffset; +import org.springframework.data.redis.connection.stream.StreamReadOptions; +import org.springframework.data.redis.connection.stream.StringRecord; import org.springframework.data.redis.connection.zset.Aggregate; import org.springframework.data.redis.connection.zset.DefaultTuple; import org.springframework.data.redis.connection.zset.Tuple; @@ -2566,35 +2576,36 @@ public Cursor> hScan(byte[] key, ScanOptions options) { return this.delegate.hScan(key, options); } - @Nullable @Override public Long hStrLen(byte[] key, byte[] field) { return convertAndReturn(delegate.hStrLen(key, field), Converters.identityConverter()); } - public @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, + public @Nullable List applyExpiration(byte[] key, + org.springframework.data.redis.core.types.Expiration expiration, FieldExpirationOptions options, byte[]... fields) { - return this.delegate.expireHashField(key, expiration, options, fields); + return this.delegate.applyExpiration(key, expiration, options, fields); } @Override - public List hExpire(byte[] key, long seconds, byte[]... fields) { - return this.delegate.hExpire(key, seconds, fields); + public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { + return this.delegate.hExpire(key, seconds, condition, fields); } @Override - public List hpExpire(byte[] key, long millis, byte[]... fields) { - return this.delegate.hpExpire(key, millis, fields); + public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { + return this.delegate.hpExpire(key, millis, condition, fields); } @Override - public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { - return this.delegate.hExpireAt(key, unixTime, fields); + public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) { + return this.delegate.hExpireAt(key, unixTime, condition, fields); } @Override - public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { - return this.delegate.hpExpireAt(key, unixTimeInMillis, fields); + public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + byte[]... fields) { + return this.delegate.hpExpireAt(key, unixTimeInMillis, condition, fields); } @Override @@ -2617,29 +2628,31 @@ public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { return this.delegate.hTtl(key, timeUnit, fields); } - public @Nullable List expireHashField(String key, org.springframework.data.redis.core.types.Expiration expiration, + public @Nullable List applyExpiration(String key, + org.springframework.data.redis.core.types.Expiration expiration, FieldExpirationOptions options, String... fields) { - return expireHashField(serialize(key), expiration, options, serializeMulti(fields)); + return applyExpiration(serialize(key), expiration, options, serializeMulti(fields)); } @Override - public List hExpire(String key, long seconds, String... fields) { - return hExpire(serialize(key), seconds, serializeMulti(fields)); + public List hExpire(String key, long seconds, FieldExpirationOptions.Condition condition, String... fields) { + return hExpire(serialize(key), seconds, condition, serializeMulti(fields)); } @Override - public List hpExpire(String key, long millis, String... fields) { - return hpExpire(serialize(key), millis, serializeMulti(fields)); + public List hpExpire(String key, long millis, FieldExpirationOptions.Condition condition, String... fields) { + return hpExpire(serialize(key), millis, condition, serializeMulti(fields)); } @Override - public List hExpireAt(String key, long unixTime, String... fields) { - return hExpireAt(serialize(key), unixTime, serializeMulti(fields)); + public List hExpireAt(String key, long unixTime, FieldExpirationOptions.Condition condition, String... fields) { + return hExpireAt(serialize(key), unixTime, condition, serializeMulti(fields)); } @Override - public List hpExpireAt(String key, long unixTimeInMillis, String... fields) { - return hpExpireAt(serialize(key), unixTimeInMillis, serializeMulti(fields)); + public List hpExpireAt(String key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + String... fields) { + return hpExpireAt(serialize(key), unixTimeInMillis, condition, serializeMulti(fields)); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java index 979cf53009..8190caa6a0 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -1483,28 +1483,58 @@ default Long hStrLen(byte[] key, byte[] field) { @Override @Deprecated default List hExpire(byte[] key, long seconds, byte[]... fields) { - return hashCommands().hExpire(key, seconds, fields); + return hashCommands().hExpire(key, seconds, FieldExpirationOptions.Condition.ALWAYS, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { + return hashCommands().hExpire(key, seconds, condition, fields); } /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @Override @Deprecated default List hpExpire(byte[] key, long millis, byte[]... fields) { - return hashCommands().hpExpire(key, millis, fields); + return hashCommands().hpExpire(key, millis, FieldExpirationOptions.Condition.ALWAYS, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { + return hashCommands().hpExpire(key, millis, condition, fields); } /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @Override @Deprecated default List hExpireAt(byte[] key, long unixTime, byte[]... fields) { - return hashCommands().hExpireAt(key, unixTime, fields); + return hashCommands().hExpireAt(key, unixTime, FieldExpirationOptions.Condition.ALWAYS, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, + byte[]... fields) { + return hashCommands().hExpireAt(key, unixTime, condition, fields); } /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @Override @Deprecated default List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { - return hashCommands().hpExpireAt(key, unixTimeInMillis, fields); + return hashCommands().hpExpireAt(key, unixTimeInMillis, FieldExpirationOptions.Condition.ALWAYS, fields); + } + + /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ + @Override + @Deprecated + default List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + byte[]... fields) { + return hashCommands().hpExpireAt(key, unixTimeInMillis, condition, fields); } /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @@ -1538,9 +1568,10 @@ default List hpTtl(byte[] key, byte[]... fields) { /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @Override @Deprecated - default @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, - FieldExpirationOptions options, byte[]... fields) { - return hashCommands().expireHashField(key, expiration, options, fields); + default @Nullable List applyExpiration(byte[] key, + org.springframework.data.redis.core.types.Expiration expiration, FieldExpirationOptions options, + byte[]... fields) { + return hashCommands().applyExpiration(key, expiration, options, fields); } // GEO COMMANDS @@ -1914,9 +1945,8 @@ default T evalSha(byte[] scriptSha, ReturnType returnType, int numKeys, byte /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */ @Override @Deprecated - default Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit) { + default Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { return zSetCommands().zRangeStoreByLex(dstKey, srcKey, range, limit); } @@ -1933,7 +1963,7 @@ default Long zRangeStoreRevByLex(byte[] dstKey, byte[] srcKey, org.springframewo @Deprecated default Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit) { + org.springframework.data.redis.connection.Limit limit) { return zSetCommands().zRangeStoreByScore(dstKey, srcKey, range, limit); } diff --git a/src/main/java/org/springframework/data/redis/connection/Hash.java b/src/main/java/org/springframework/data/redis/connection/Hash.java index c8d0e8a248..51e326dd2b 100644 --- a/src/main/java/org/springframework/data/redis/connection/Hash.java +++ b/src/main/java/org/springframework/data/redis/connection/Hash.java @@ -18,21 +18,25 @@ import java.util.Objects; import org.springframework.lang.Contract; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** + * Types for interacting with Hash data structures. + * * @author Christoph Strobl * @since 3.5 */ public interface Hash { + /** + * Expiration options for Hash Expiation updates. + */ class FieldExpirationOptions { - private static final FieldExpirationOptions NONE = new FieldExpirationOptions(null); - private @Nullable Condition condition; + private static final FieldExpirationOptions NONE = new FieldExpirationOptions(Condition.ALWAYS); + private final Condition condition; - FieldExpirationOptions(@Nullable Condition condition) { + FieldExpirationOptions(Condition condition) { this.condition = condition; } @@ -40,12 +44,11 @@ public static FieldExpirationOptions none() { return NONE; } - @Contract("_ -> new") public static FieldExpireOptionsBuilder builder() { return new FieldExpireOptionsBuilder(); } - public @Nullable Condition getCondition() { + public Condition getCondition() { return condition; } @@ -68,48 +71,67 @@ public int hashCode() { public static class FieldExpireOptionsBuilder { - @Nullable Condition condition; + private Condition condition = Condition.ALWAYS; - @Contract("_ -> this") + @Contract("-> this") public FieldExpireOptionsBuilder nx() { this.condition = Condition.NX; return this; } - @Contract("_ -> this") + @Contract("-> this") public FieldExpireOptionsBuilder xx() { this.condition = Condition.XX; return this; } - @Contract("_ -> this") + @Contract("-> this") public FieldExpireOptionsBuilder gt() { this.condition = Condition.GT; return this; } - @Contract("_ -> this") + @Contract("-> this") public FieldExpireOptionsBuilder lt() { this.condition = Condition.LT; return this; } - @Contract("_ -> !null") public FieldExpirationOptions build() { - return condition == null ? NONE : new FieldExpirationOptions(condition); + return condition == Condition.ALWAYS ? NONE : new FieldExpirationOptions(condition); } + } public enum Condition { - /** Set expiration only when the field has no expiration. */ + /** + * Always apply expiration. + */ + ALWAYS, + + /** + * Set expiration only when the field has no expiration. + */ NX, - /** Set expiration only when the field has an existing expiration. */ + + /** + * Set expiration only when the field has an existing expiration. + */ XX, - /** Set expiration only when the new expiration is greater than current one. */ + + /** + * Set expiration only when the new expiration is greater than current one. + */ GT, - /** Set expiration only when the new expiration is greater than current one. */ + + /** + * Set expiration only when the new expiration is greater than current one. + */ LT + } + } + } diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java index f58f4e32a9..1e9fd94f3a 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java @@ -858,7 +858,9 @@ class ExpireCommand extends HashFieldsCommand { private ExpireCommand(@Nullable ByteBuffer key, List fields, Expiration expiration, FieldExpirationOptions options) { + super(key, fields); + this.expiration = expiration; this.options = options; } @@ -921,6 +923,7 @@ public FieldExpirationOptions getOptions() { default Mono hExpire(ByteBuffer key, Duration duration, ByteBuffer field) { Assert.notNull(duration, "Duration must not be null"); + return hExpire(key, duration, Collections.singletonList(field)).singleOrEmpty(); } @@ -939,9 +942,10 @@ default Mono hExpire(ByteBuffer key, Duration duration, ByteBuffer field) * @since 3.5 */ default Flux hExpire(ByteBuffer key, Duration duration, List fields) { + Assert.notNull(duration, "Duration must not be null"); - return expireHashField(Flux.just(ExpireCommand.expire(fields, duration).from(key))) + return applyExpiration(Flux.just(ExpireCommand.expire(fields, duration).from(key))) .mapNotNull(NumericResponse::getOutput); } @@ -957,7 +961,7 @@ default Flux hExpire(ByteBuffer key, Duration duration, List f * @since 3.5 * @see Redis Documentation: HEXPIRE */ - Flux> expireHashField(Publisher commands); + Flux> applyExpiration(Publisher commands); /** * Expire a given {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. @@ -975,6 +979,7 @@ default Flux hExpire(ByteBuffer key, Duration duration, List f default Mono hpExpire(ByteBuffer key, Duration duration, ByteBuffer field) { Assert.notNull(duration, "Duration must not be null"); + return hpExpire(key, duration, Collections.singletonList(field)).singleOrEmpty(); } @@ -995,7 +1000,8 @@ default Mono hpExpire(ByteBuffer key, Duration duration, ByteBuffer field) default Flux hpExpire(ByteBuffer key, Duration duration, List fields) { Assert.notNull(duration, "Duration must not be null"); - return expireHashField(Flux.just(new ExpireCommand(key, fields, + + return applyExpiration(Flux.just(new ExpireCommand(key, fields, Expiration.from(duration.toMillis(), TimeUnit.MILLISECONDS), FieldExpirationOptions.none()))) .mapNotNull(NumericResponse::getOutput); } @@ -1017,6 +1023,7 @@ default Flux hpExpire(ByteBuffer key, Duration duration, List default Mono hExpireAt(ByteBuffer key, Instant expireAt, ByteBuffer field) { Assert.notNull(expireAt, "Duration must not be null"); + return hExpireAt(key, expireAt, Collections.singletonList(field)).singleOrEmpty(); } @@ -1035,9 +1042,10 @@ default Mono hExpireAt(ByteBuffer key, Instant expireAt, ByteBuffer field) * @since 3.5 */ default Flux hExpireAt(ByteBuffer key, Instant expireAt, List fields) { + Assert.notNull(expireAt, "Duration must not be null"); - return expireHashField(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.SECONDS).from(key))) + return applyExpiration(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.SECONDS).from(key))) .mapNotNull(NumericResponse::getOutput); } @@ -1058,6 +1066,7 @@ default Flux hExpireAt(ByteBuffer key, Instant expireAt, List default Mono hpExpireAt(ByteBuffer key, Instant expireAt, ByteBuffer field) { Assert.notNull(expireAt, "Duration must not be null"); + return hpExpireAt(key, expireAt, Collections.singletonList(field)).singleOrEmpty(); } @@ -1076,9 +1085,10 @@ default Mono hpExpireAt(ByteBuffer key, Instant expireAt, ByteBuffer field * @since 3.5 */ default Flux hpExpireAt(ByteBuffer key, Instant expireAt, List fields) { + Assert.notNull(expireAt, "Duration must not be null"); - return expireHashField(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.MILLISECONDS).from(key))) + return applyExpiration(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.MILLISECONDS).from(key))) .mapNotNull(NumericResponse::getOutput); } @@ -1135,7 +1145,6 @@ default Flux hPersist(ByteBuffer key, List fields) { * @since 3.5 */ default Mono hTtl(ByteBuffer key, ByteBuffer field) { - return hTtl(key, Collections.singletonList(field)).singleOrEmpty(); } @@ -1151,7 +1160,6 @@ default Mono hTtl(ByteBuffer key, ByteBuffer field) { * @since 3.5 */ default Flux hTtl(ByteBuffer key, List fields) { - return hTtl(Flux.just(new HashFieldsCommand(key, fields))).mapNotNull(NumericResponse::getOutput); } @@ -1179,7 +1187,6 @@ default Flux hTtl(ByteBuffer key, List fields) { * @since 3.5 */ default Mono hpTtl(ByteBuffer key, ByteBuffer field) { - return hpTtl(key, Collections.singletonList(field)).singleOrEmpty(); } @@ -1195,7 +1202,6 @@ default Mono hpTtl(ByteBuffer key, ByteBuffer field) { * @since 3.5 */ default Flux hpTtl(ByteBuffer key, List fields) { - return hpTtl(Flux.just(new HashFieldsCommand(key, fields))).mapNotNull(NumericResponse::getOutput); } @@ -1210,4 +1216,5 @@ default Flux hpTtl(ByteBuffer key, List fields) { * @see Redis Documentation: HPTTL */ Flux> hpTtl(Publisher commands); + } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java index 5fde9d5db3..f2d736b8ef 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java @@ -25,6 +25,7 @@ import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; import org.springframework.lang.Nullable; +import org.springframework.util.ObjectUtils; /** * Hash-specific commands supported by Redis. @@ -254,41 +255,78 @@ public interface RedisHashCommands { @Nullable Long hStrLen(byte[] key, byte[] field); - default @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, - byte[]... fields) { - return expireHashField(key, expiration, FieldExpirationOptions.none(), fields); + default @Nullable List applyExpiration(byte[] key, + org.springframework.data.redis.core.types.Expiration expiration, byte[]... fields) { + return applyExpiration(key, expiration, FieldExpirationOptions.none(), fields); } + @Nullable + default List applyExpiration(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, + FieldExpirationOptions options, byte[]... fields) { + + if (expiration.isPersistent()) { + return hPersist(key, fields); + } + + if (ObjectUtils.nullSafeEquals(FieldExpirationOptions.none(), options)) { + if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { + if (expiration.isUnixTimestamp()) { + return hpExpireAt(key, expiration.getExpirationTimeInMilliseconds(), fields); + } + return hpExpire(key, expiration.getExpirationTimeInMilliseconds(), fields); + } + if (expiration.isUnixTimestamp()) { + return hExpireAt(key, expiration.getExpirationTimeInSeconds(), fields); + } + return hExpire(key, expiration.getExpirationTimeInSeconds(), fields); + } + + if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { + if (expiration.isUnixTimestamp()) { + return hpExpireAt(key, expiration.getExpirationTimeInMilliseconds(), options.getCondition(), fields); + } + + return hpExpire(key, expiration.getExpirationTimeInMilliseconds(), options.getCondition(), fields); + } + + if (expiration.isUnixTimestamp()) { + return hExpireAt(key, expiration.getExpirationTimeInSeconds(), options.getCondition(), fields); + } + + return hExpire(key, expiration.getExpirationTimeInSeconds(), options.getCondition(), fields); + } - @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, - FieldExpirationOptions options, byte[]... fields); - - /** - * Set time to live for given {@code fields} in seconds. - * - * @param key must not be {@literal null}. - * @param seconds the amount of time after which the fields will be expired in seconds, must not be {@literal null}. - * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. - * @see Redis Documentation: HEXPIRE - * @since 3.5 - */ + /** + * Set time to live for given {@code fields} in seconds. + * + * @param key must not be {@literal null}. + * @param seconds the amount of time after which the fields will be expired in seconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ @Nullable - List hExpire(byte[] key, long seconds, byte[]... fields); + default List hExpire(byte[] key, long seconds, byte[]... fields) { + return hExpire(key, seconds, FieldExpirationOptions.Condition.ALWAYS, fields); + } /** * Set time to live for given {@code fields}. * * @param key must not be {@literal null}. - * @param ttl the amount of time after which the fields will be expired in {@link Duration#toSeconds() seconds} precision, must not be {@literal null}. + * @param ttl the amount of time after which the fields will be expired in {@link Duration#toSeconds() seconds} + * precision, must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HEXPIRE * @since 3.5 */ @@ -297,32 +335,56 @@ default List hExpire(byte[] key, Duration ttl, byte[]... fields) { return hExpire(key, ttl.toSeconds(), fields); } + /** + * Set time to live for given {@code fields} in seconds. + * + * @param key must not be {@literal null}. + * @param seconds the amount of time after which the fields will be expired in seconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @param condition the condition for expiration, must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable + List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields); + /** * Set time to live for given {@code fields} in milliseconds. * * @param key must not be {@literal null}. - * @param millis the amount of time after which the fields will be expired in milliseconds, must not be {@literal null}. + * @param millis the amount of time after which the fields will be expired in milliseconds, must not be + * {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HPEXPIRE * @since 3.5 */ @Nullable - List hpExpire(byte[] key, long millis, byte[]... fields); + default List hpExpire(byte[] key, long millis, byte[]... fields) { + return hpExpire(key, millis, FieldExpirationOptions.Condition.ALWAYS, fields); + } /** * Set time to live for given {@code fields} in milliseconds. * * @param key must not be {@literal null}. - * @param ttl the amount of time after which the fields will be expired in {@link Duration#toMillis() milliseconds} precision, must not be {@literal null}. + * @param ttl the amount of time after which the fields will be expired in {@link Duration#toMillis() milliseconds} + * precision, must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HPEXPIRE * @since 3.5 */ @@ -331,46 +393,109 @@ default List hpExpire(byte[] key, Duration ttl, byte[]... fields) { return hpExpire(key, ttl.toMillis(), fields); } + /** + * Set time to live for given {@code fields} in milliseconds. + * + * @param key must not be {@literal null}. + * @param millis the amount of time after which the fields will be expired in milliseconds, must not be + * {@literal null}. + * @param condition the condition for expiration, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: HPEXPIRE + * @since 3.5 + */ + @Nullable + List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields); + /** * Set the expiration for given {@code field} as a {@literal UNIX} timestamp. * * @param key must not be {@literal null}. * @param unixTime the moment in time in which the field expires, must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. * @see Redis Documentation: HEXPIREAT * @since 3.5 */ @Nullable - List hExpireAt(byte[] key, long unixTime, byte[]... fields); + default List hExpireAt(byte[] key, long unixTime, byte[]... fields) { + return hExpireAt(key, unixTime, FieldExpirationOptions.Condition.ALWAYS, fields); + } + + /** + * Set the expiration for given {@code field} as a {@literal UNIX} timestamp. + * + * @param key must not be {@literal null}. + * @param unixTime the moment in time in which the field expires, must not be {@literal null}. + * @param condition the condition for expiration, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. + * @see Redis Documentation: HEXPIREAT + * @since 3.5 + */ + @Nullable + List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields); + + /** + * Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds. + * + * @param key must not be {@literal null}. + * @param unixTimeInMillis the moment in time in which the field expires in milliseconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. + * @see Redis Documentation: HPEXPIREAT + * @since 3.5 + */ + @Nullable + default List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { + return hpExpireAt(key, unixTimeInMillis, FieldExpirationOptions.Condition.ALWAYS, fields); + } /** * Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds. * * @param key must not be {@literal null}. * @param unixTimeInMillis the moment in time in which the field expires in milliseconds, must not be {@literal null}. + * @param condition the condition for expiration, must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. * @see Redis Documentation: HPEXPIREAT * @since 3.5 */ @Nullable - List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields); + List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + byte[]... fields); /** * Remove the expiration from given {@code field}. * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; - * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; - * {@literal null} when used in pipeline / transaction.{@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is + * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction.{@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HPERSIST * @since 3.5 */ @@ -382,9 +507,10 @@ default List hpExpire(byte[] key, Duration ttl, byte[]... fields) { * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the field exists but has no associated expiration time. - * The command returns {@code -2} if the field does not exist; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative + * value to signal an error. The command returns {@code -1} if the field exists but has no associated + * expiration time. The command returns {@code -2} if the field does not exist; {@literal null} when used in + * pipeline / transaction. * @see Redis Documentation: HTTL * @since 3.5 */ @@ -397,9 +523,10 @@ default List hpExpire(byte[] key, Duration ttl, byte[]... fields) { * @param key must not be {@literal null}. * @param timeUnit must not be {@literal null}. * @param fields must not be {@literal null}. - * @return for each of the fields supplied - the time to live in the {@link TimeUnit} provided; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @return for each of the fields supplied - the time to live in the {@link TimeUnit} provided; or a negative value to + * signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. + * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HTTL * @since 3.5 */ @@ -413,9 +540,10 @@ default List hpExpire(byte[] key, Duration ttl, byte[]... fields) { * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative + * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration + * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HTTL * @since 3.5 */ diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java index ed0101641e..5ff0e9946f 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -71,7 +71,6 @@ * @author Andrey Shlykov * @author ihaohong * @author Shyngys Sapraliyev - * * @see RedisCallback * @see RedisSerializer * @see StringRedisTemplate @@ -1661,7 +1660,6 @@ default Long lPos(String key, String element) { */ Long zRemRange(String key, long start, long end); - /** * Remove all elements between the lexicographical {@link Range}. * @@ -1941,7 +1939,8 @@ default Set zUnionWithScores(Aggregate aggregate, int[] weights, St * @return * @since 1.6 * @see Redis Documentation: ZRANGEBYLEX - * @see RedisZSetCommands#zRangeByLex(byte[], org.springframework.data.domain.Range, org.springframework.data.redis.connection.Limit) + * @see RedisZSetCommands#zRangeByLex(byte[], org.springframework.data.domain.Range, + * org.springframework.data.redis.connection.Limit) */ Set zRangeByLex(String key, org.springframework.data.domain.Range range, org.springframework.data.redis.connection.Limit limit); @@ -1983,7 +1982,8 @@ default Set zRevRangeByLex(String key, org.springframework.data.domain.R * @return * @since 2.4 * @see Redis Documentation: ZREVRANGEBYLEX - * @see RedisZSetCommands#zRevRangeByLex(byte[], org.springframework.data.domain.Range, org.springframework.data.redis.connection.Limit) + * @see RedisZSetCommands#zRevRangeByLex(byte[], org.springframework.data.domain.Range, + * org.springframework.data.redis.connection.Limit) */ Set zRevRangeByLex(String key, org.springframework.data.domain.Range range, org.springframework.data.redis.connection.Limit limit); @@ -2333,22 +2333,41 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, @Nullable Long hStrLen(String key, String field); - // TODO: why why whay is this such a shitty api that there's missing all the NX, XX, GT Options /** * Set time to live for given {@code field} in seconds. * * @param key must not be {@literal null}. * @param seconds the amount of time after which the key will be expired in seconds, must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HEXPIRE - * @since 3.4 + * @since 3.5 + */ + @Nullable + default List hExpire(String key, long seconds, String... fields) { + return hExpire(key, seconds, Hash.FieldExpirationOptions.Condition.ALWAYS, fields); + } + + /** + * Set time to live for given {@code field} in seconds. + * + * @param key must not be {@literal null}. + * @param seconds the amount of time after which the key will be expired in seconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: HEXPIRE + * @since 3.5 */ @Nullable - List hExpire(String key, long seconds, String... fields); + List hExpire(String key, long seconds, Hash.FieldExpirationOptions.Condition condition, String... fields); /** * Set time to live for given {@code field} in milliseconds. @@ -2356,15 +2375,54 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, * @param key must not be {@literal null}. * @param millis the amount of time after which the key will be expired in milliseconds, must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: HPEXPIRE + * @since 3.5 + */ + @Nullable + default List hpExpire(String key, long millis, String... fields) { + return hpExpire(key, millis, Hash.FieldExpirationOptions.Condition.ALWAYS, fields); + } + + /** + * Set time to live for given {@code field} in milliseconds. + * + * @param key must not be {@literal null}. + * @param millis the amount of time after which the key will be expired in milliseconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HPEXPIRE - * @since 3.4 + * @since 3.5 + */ + @Nullable + List hpExpire(String key, long millis, Hash.FieldExpirationOptions.Condition condition, String... fields); + + /** + * Set the expiration for given {@code field} as a {@literal UNIX} timestamp. + * + * @param key must not be {@literal null}. + * @param unixTime the moment in time in which the field expires, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. + * @see Redis Documentation: HEXPIREAT + * @since 3.5 */ @Nullable - List hpExpire(String key, long millis, String... fields); + default List hExpireAt(String key, long unixTime, String... fields) { + return hExpireAt(key, unixTime, Hash.FieldExpirationOptions.Condition.ALWAYS, fields); + } /** * Set the expiration for given {@code field} as a {@literal UNIX} timestamp. @@ -2372,15 +2430,16 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, * @param key must not be {@literal null}. * @param unixTime the moment in time in which the field expires, must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. * @see Redis Documentation: HEXPIREAT - * @since 3.4 + * @since 3.5 */ @Nullable - List hExpireAt(String key, long unixTime, String... fields); + List hExpireAt(String key, long unixTime, Hash.FieldExpirationOptions.Condition condition, String... fields); /** * Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds. @@ -2388,26 +2447,48 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, * @param key must not be {@literal null}. * @param unixTimeInMillis the moment in time in which the field expires in milliseconds, must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not - * met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. * @see Redis Documentation: HPEXPIREAT - * @since 3.4 + * @since 3.5 */ @Nullable - List hpExpireAt(String key, long unixTimeInMillis, String... fields); + default List hpExpireAt(String key, long unixTimeInMillis, String... fields) { + return hpExpireAt(key, unixTimeInMillis, Hash.FieldExpirationOptions.Condition.ALWAYS, fields); + } + + /** + * Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds. + * + * @param key must not be {@literal null}. + * @param unixTimeInMillis the moment in time in which the field expires in milliseconds, must not be {@literal null}. + * @param fields must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. + * @see Redis Documentation: HPEXPIREAT + * @since 3.5 + */ + @Nullable + List hpExpireAt(String key, long unixTimeInMillis, Hash.FieldExpirationOptions.Condition condition, + String... fields); /** * Remove the expiration from given {@code field}. * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; - * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; - * {@literal null} when used in pipeline / transaction.{@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is + * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction.{@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HPERSIST - * @since 3.4 + * @since 3.5 */ @Nullable List hPersist(String key, String... fields); @@ -2417,9 +2498,10 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in milliseconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: the time to live in milliseconds; or a + * negative value to signal an error. The command returns {@code -1} if the key exists but has no associated + * expiration time. The command returns {@code -2} if the key does not exist; {@literal null} when used in + * pipeline / transaction. * @see Redis Documentation: HTTL * @since 3.5 */ @@ -2432,9 +2514,10 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, * @param key must not be {@literal null}. * @param timeUnit must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in the {@link TimeUnit} provided; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: the time to live in the {@link TimeUnit} + * provided; or a negative value to signal an error. The command returns {@code -1} if the key exists but has + * no associated expiration time. The command returns {@code -2} if the key does not exist; {@literal null} + * when used in pipeline / transaction. * @see Redis Documentation: HTTL * @since 3.5 */ @@ -2446,9 +2529,10 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, * * @param key must not be {@literal null}. * @param fields must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in milliseconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. - * The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: the time to live in milliseconds; or a + * negative value to signal an error. The command returns {@code -1} if the key exists but has no associated + * expiration time. The command returns {@code -2} if the key does not exist; {@literal null} when used in + * pipeline / transaction. * @see Redis Documentation: HTTL * @since 3.5 */ @@ -2678,8 +2762,7 @@ GeoResults> geoRadiusByMember(String key, String member, Dis /** * Return the members of a geo set which are within the borders of the area specified by a given {@link GeoShape - * shape}. The query's center point is provided by - * {@link GeoReference}. + * shape}. The query's center point is provided by {@link GeoReference}. * * @param key must not be {@literal null}. * @param reference must not be {@literal null}. @@ -2695,8 +2778,7 @@ GeoResults> geoSearch(String key, GeoReference refer /** * Query the members of a geo set which are within the borders of the area specified by a given {@link GeoShape shape} - * and store the result at {@code destKey}. The query's center point is provided by - * {@link GeoReference}. + * and store the result at {@code destKey}. The query's center point is provided by {@link GeoReference}. * * @param key must not be {@literal null}. * @param reference must not be {@literal null}. diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java index 3326a00d62..1223ab4c06 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java @@ -33,10 +33,8 @@ import org.springframework.data.redis.core.ScanCursor; import org.springframework.data.redis.core.ScanIteration; import org.springframework.data.redis.core.ScanOptions; -import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; /** * Cluster {@link RedisHashCommands} implementation for Jedis. @@ -292,88 +290,71 @@ protected ScanIteration> doScan(CursorId cursorId, ScanOpt }.open(); } - @Nullable @Override - public List expireHashField(byte[] key, Expiration expiration, FieldExpirationOptions options, - byte[]... fields) { - - if (expiration.isPersistent()) { - return hPersist(key, fields); - } - - if (ObjectUtils.nullSafeEquals(FieldExpirationOptions.none(), options)) { - if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { - if (expiration.isUnixTimestamp()) { - return hpExpireAt(key, expiration.getExpirationTimeInMilliseconds(), fields); - } - return hpExpire(key, expiration.getExpirationTimeInMilliseconds(), fields); - } - if (expiration.isUnixTimestamp()) { - return hExpireAt(key, expiration.getExpirationTimeInSeconds(), fields); - } - return hExpire(key, expiration.getExpirationTimeInSeconds(), fields); - } - - ExpiryOption option = ExpiryOption.valueOf(options.getCondition().name()); - - if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { - if (expiration.isUnixTimestamp()) { - return connection.getCluster().hpexpireAt(key, expiration.getExpirationTimeInMilliseconds(), option, fields); - } - return connection.getCluster().hpexpire(key, expiration.getExpirationTimeInMilliseconds(), option, fields); - } - - if (expiration.isUnixTimestamp()) { - return connection.getCluster().hexpireAt(key, expiration.getExpirationTimeInSeconds(), option, fields); - } - return connection.getCluster().hexpire(key, expiration.getExpirationTimeInSeconds(), option, fields); - - } - - @Override - public List hExpire(byte[] key, long seconds, byte[]... fields) { + public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); try { - return connection.getCluster().hexpire(key, seconds, fields); + if (condition == FieldExpirationOptions.Condition.ALWAYS) { + return connection.getCluster().hexpire(key, seconds, fields); + } + + return connection.getCluster().hexpire(key, seconds, ExpiryOption.valueOf(condition.name()), fields); } catch (Exception ex) { throw convertJedisAccessException(ex); } } @Override - public List hpExpire(byte[] key, long millis, byte[]... fields) { + public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); try { - return connection.getCluster().hpexpire(key, millis, fields); + if (condition == FieldExpirationOptions.Condition.ALWAYS) { + return connection.getCluster().hpexpire(key, millis, fields); + } + + return connection.getCluster().hpexpire(key, millis, ExpiryOption.valueOf(condition.name()), fields); } catch (Exception ex) { throw convertJedisAccessException(ex); } } @Override - public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { + public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) { Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); try { - return connection.getCluster().hexpireAt(key, unixTime, fields); + + if (condition == FieldExpirationOptions.Condition.ALWAYS) { + return connection.getCluster().hexpireAt(key, unixTime, fields); + } + + return connection.getCluster().hexpireAt(key, unixTime, ExpiryOption.valueOf(condition.name()), fields); } catch (Exception ex) { throw convertJedisAccessException(ex); } } @Override - public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { + public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + byte[]... fields) { + Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); try { - return connection.getCluster().hpexpireAt(key, unixTimeInMillis, fields); + + if (condition == FieldExpirationOptions.Condition.ALWAYS) { + return connection.getCluster().hpexpireAt(key, unixTimeInMillis, fields); + } + + return connection.getCluster().hpexpireAt(key, unixTimeInMillis, ExpiryOption.valueOf(condition.name()), fields); } catch (Exception ex) { throw convertJedisAccessException(ex); } @@ -381,6 +362,7 @@ public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields @Override public List hPersist(byte[] key, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); @@ -393,6 +375,7 @@ public List hPersist(byte[] key, byte[]... fields) { @Override public List hTtl(byte[] key, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); @@ -405,6 +388,7 @@ public List hTtl(byte[] key, byte[]... fields) { @Override public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); @@ -418,6 +402,7 @@ public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { @Override public List hpTtl(byte[] key, byte[]... fields) { + Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); @@ -439,7 +424,7 @@ public Long hStrLen(byte[] key, byte[] field) { } private DataAccessException convertJedisAccessException(Exception ex) { - return connection.convertJedisAccessException(ex); } + } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java index e8751e85cb..2e83d8aba0 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java @@ -39,7 +39,6 @@ import org.springframework.data.redis.core.ScanOptions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; /** * {@link RedisHashCommands} implementation for Jedis. @@ -257,62 +256,48 @@ protected void doClose() { } @Override - public List hExpire(byte[] key, long seconds, byte[]... fields) { - return connection.invoke().just(Jedis::hexpire, PipelineBinaryCommands::hexpire, key, seconds, fields); - } + public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { - @Override - public List hpExpire(byte[] key, long millis, byte[]... fields) { - return connection.invoke().just(Jedis::hpexpire, PipelineBinaryCommands::hpexpire, key, millis, fields); + if (condition == FieldExpirationOptions.Condition.ALWAYS) { + return connection.invoke().just(Jedis::hexpire, PipelineBinaryCommands::hexpire, key, seconds, fields); + } + + ExpiryOption option = ExpiryOption.valueOf(condition.name()); + return connection.invoke().just(Jedis::hexpire, PipelineBinaryCommands::hexpire, key, seconds, option, fields); } @Override - public @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, - FieldExpirationOptions options, byte[]... fields) { + public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { - if (expiration.isPersistent()) { - return hPersist(key, fields); + if (condition == FieldExpirationOptions.Condition.ALWAYS) { + return connection.invoke().just(Jedis::hpexpire, PipelineBinaryCommands::hpexpire, key, millis, fields); } - if (ObjectUtils.nullSafeEquals(FieldExpirationOptions.none(), options)) { - if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { - if (expiration.isUnixTimestamp()) { - return hpExpireAt(key, expiration.getExpirationTimeInMilliseconds(), fields); - } - return hpExpire(key, expiration.getExpirationTimeInMilliseconds(), fields); - } - if (expiration.isUnixTimestamp()) { - return hExpireAt(key, expiration.getExpirationTimeInSeconds(), fields); - } - return hExpire(key, expiration.getExpirationTimeInSeconds(), fields); - } + ExpiryOption option = ExpiryOption.valueOf(condition.name()); + return connection.invoke().just(Jedis::hpexpire, PipelineBinaryCommands::hpexpire, key, millis, option, fields); + } - ExpiryOption option = ExpiryOption.valueOf(options.getCondition().name()); + @Override + public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) { - if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { - if (expiration.isUnixTimestamp()) { - return connection.invoke().just(Jedis::hpexpireAt, PipelineBinaryCommands::hpexpireAt, key, - expiration.getExpirationTimeInMilliseconds(), option, fields); - } - return connection.invoke().just(Jedis::hpexpire, PipelineBinaryCommands::hpexpire, key, - expiration.getExpirationTimeInMilliseconds(), option, fields); + if (condition == FieldExpirationOptions.Condition.ALWAYS) { + return connection.invoke().just(Jedis::hexpireAt, PipelineBinaryCommands::hexpireAt, key, unixTime, fields); } - if (expiration.isUnixTimestamp()) { - return connection.invoke().just(Jedis::hexpireAt, PipelineBinaryCommands::hexpireAt, key, - expiration.getExpirationTimeInSeconds(), option, fields); - } - return connection.invoke().just(Jedis::hexpire, PipelineBinaryCommands::hexpire, key, - expiration.getExpirationTimeInSeconds(), option, fields); + ExpiryOption option = ExpiryOption.valueOf(condition.name()); + return connection.invoke().just(Jedis::hexpireAt, PipelineBinaryCommands::hexpireAt, key, unixTime, option, fields); } @Override - public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { - return connection.invoke().just(Jedis::hexpireAt, PipelineBinaryCommands::hexpireAt, key, unixTime, fields); - } + public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + byte[]... fields) { - @Override - public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { + if (condition == FieldExpirationOptions.Condition.ALWAYS) { + return connection.invoke().just(Jedis::hpexpireAt, PipelineBinaryCommands::hpexpireAt, key, unixTimeInMillis, + fields); + } + + ExpiryOption option = ExpiryOption.valueOf(condition.name()); return connection.invoke().just(Jedis::hpexpireAt, PipelineBinaryCommands::hpexpireAt, key, unixTimeInMillis, fields); } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java index fc8460d514..fe646b3a1b 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java @@ -1160,6 +1160,14 @@ static class TypeHints { COMMAND_OUTPUT_TYPE_MAPPING.put(PFMERGE, IntegerOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(PFADD, IntegerOutput.class); + COMMAND_OUTPUT_TYPE_MAPPING.put(HEXPIRE, IntegerListOutput.class); + COMMAND_OUTPUT_TYPE_MAPPING.put(HEXPIREAT, IntegerListOutput.class); + COMMAND_OUTPUT_TYPE_MAPPING.put(HPEXPIRE, IntegerListOutput.class); + COMMAND_OUTPUT_TYPE_MAPPING.put(HPEXPIREAT, IntegerListOutput.class); + COMMAND_OUTPUT_TYPE_MAPPING.put(HPERSIST, IntegerListOutput.class); + COMMAND_OUTPUT_TYPE_MAPPING.put(HTTL, IntegerListOutput.class); + COMMAND_OUTPUT_TYPE_MAPPING.put(HPTTL, IntegerListOutput.class); + // DOUBLE COMMAND_OUTPUT_TYPE_MAPPING.put(HINCRBYFLOAT, DoubleOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(INCRBYFLOAT, DoubleOutput.class); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java index 16564fd1eb..032d6230d6 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java @@ -215,63 +215,28 @@ public Cursor> hScan(byte[] key, ScanOptions options) { } @Override - public @Nullable List expireHashField(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, - FieldExpirationOptions options, byte[]... fields) { - - if (expiration.isPersistent()) { - return hPersist(key, fields); - } - - ExpireArgs option = new ExpireArgs() { - @Override - public void build(CommandArgs args) { - - if(ObjectUtils.nullSafeEquals(options, FieldExpirationOptions.none())) { - return; - } - - args.add(options.getCondition().name()); - } - }; - - if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { - if (expiration.isUnixTimestamp()) { - return connection.invoke().fromMany(RedisHashAsyncCommands::hpexpireat, key, - expiration.getExpirationTimeInMilliseconds(), option, fields).toList(); - } - return connection.invoke() - .fromMany(RedisHashAsyncCommands::hpexpire, key, expiration.getExpirationTimeInMilliseconds(), option, fields) - .toList(); - } - - if (expiration.isUnixTimestamp()) { - return connection.invoke() - .fromMany(RedisHashAsyncCommands::hexpireat, key, expiration.getExpirationTimeInSeconds(), option, fields) - .toList(); - } - return connection.invoke() - .fromMany(RedisHashAsyncCommands::hexpire, key, expiration.getExpirationTimeInSeconds(), option, fields) + public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::hexpire, key, seconds, getExpireArgs(condition), fields) .toList(); } @Override - public List hExpire(byte[] key, long seconds, byte[]... fields) { - return connection.invoke().fromMany(RedisHashAsyncCommands::hexpire, key, seconds, fields).toList(); - } - - @Override - public List hpExpire(byte[] key, long millis, byte[]... fields) { - return connection.invoke().fromMany(RedisHashAsyncCommands::hpexpire, key, millis, fields).toList(); + public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { + return connection.invoke().fromMany(RedisHashAsyncCommands::hpexpire, key, millis, getExpireArgs(condition), fields) + .toList(); } @Override - public List hExpireAt(byte[] key, long unixTime, byte[]... fields) { - return connection.invoke().fromMany(RedisHashAsyncCommands::hexpireat, key, unixTime, fields).toList(); + public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) { + return connection.invoke() + .fromMany(RedisHashAsyncCommands::hexpireat, key, unixTime, getExpireArgs(condition), fields).toList(); } @Override - public List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { - return connection.invoke().fromMany(RedisHashAsyncCommands::hpexpireat, key, unixTimeInMillis, fields).toList(); + public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + byte[]... fields) { + return connection.invoke() + .fromMany(RedisHashAsyncCommands::hpexpireat, key, unixTimeInMillis, getExpireArgs(condition), fields).toList(); } @Override @@ -349,4 +314,19 @@ private static Entry toEntry(KeyValue value) { return value.hasValue() ? Converters.entryOf(value.getKey(), value.getValue()) : null; } + private ExpireArgs getExpireArgs(FieldExpirationOptions.Condition condition) { + + return new ExpireArgs() { + @Override + public void build(CommandArgs args) { + + if (ObjectUtils.nullSafeEquals(condition, FieldExpirationOptions.Condition.ALWAYS)) { + return; + } + + args.add(condition.name()); + } + }; + } + } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java index 3cc7bfd9c3..84dd2ca906 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java @@ -269,7 +269,8 @@ public Flux> hStrLen(Publisher> expireHashField(Publisher commands) { + public Flux> applyExpiration(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKey(), "Key must not be null"); @@ -317,6 +318,7 @@ public void build(CommandArgs args) { @Override public Flux> hPersist(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKey(), "Key must not be null"); @@ -329,6 +331,7 @@ public Flux> hPersist(Publisher> hTtl(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKey(), "Key must not be null"); @@ -341,6 +344,7 @@ public Flux> hTtl(Publisher> hpTtl(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKey(), "Key must not be null"); diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java new file mode 100644 index 0000000000..33e0ff82e6 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java @@ -0,0 +1,126 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.core; + +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.connection.Hash; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.core.types.Expirations; +import org.springframework.lang.Nullable; + +/** + * Hash Field Expiration operations bound to a certain hash key and set of hash fields. + * + * @param type of the hash field names. + * @author Mark Paluch + * @since 3.5 + */ +public interface BoundHashFieldExpirationOperations { + + /** + * Apply {@link Expiration} to the hash without any additional constraints. + * + * @param expiration the expiration definition. + * @return changes to the hash fields. + */ + default ExpireChanges expire(Expiration expiration) { + return expire(expiration, Hash.FieldExpirationOptions.none()); + } + + /** + * Apply {@link Expiration} to the hash fields given {@link Hash.FieldExpirationOptions expiration options}. + * + * @param expiration the expiration definition. + * @param options expiration options. + * @return changes to the hash fields. + */ + ExpireChanges expire(Expiration expiration, Hash.FieldExpirationOptions options); + + /** + * Set time to live for given {@code hashKey}. + * + * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. + * @throws IllegalArgumentException if the timeout is {@literal null}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable + ExpireChanges expire(Duration timeout); + + /** + * Set the expiration for given {@code hashKey} as a {@literal date} timestamp. + * + * @param expireAt must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. + * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable + ExpireChanges expireAt(Instant expireAt); + + /** + * Remove the expiration from given {@code hashKey} . + * + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is + * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: HPERSIST + * @since 3.5 + */ + @Nullable + ExpireChanges persist(); + + /** + * Get the time to live for {@code hashKey} in seconds. + * + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative + * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration + * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + Expirations getTimeToLive(); + + /** + * Get the time to live for {@code hashKey} and convert it to the given {@link TimeUnit}. + * + * @param timeUnit must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative + * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration + * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: HTTL + * @since 3.5 + */ + @Nullable + Expirations getTimeToLive(TimeUnit timeUnit); + +} diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java index 0503c33094..0d287e929f 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java @@ -15,16 +15,12 @@ */ package org.springframework.data.redis.core; -import java.time.Duration; -import java.time.Instant; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; -import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; /** @@ -96,7 +92,7 @@ public interface BoundHashOperations extends BoundKeyOperations { Double increment(HK key, double delta); /** - * Return a random key (aka field) from the hash stored at the bound key. + * Return a random key from the hash stored at the bound key. * * @return {@literal null} if the hash does not exist or when used in pipeline / transaction. * @since 2.6 @@ -116,10 +112,10 @@ public interface BoundHashOperations extends BoundKeyOperations { Map.Entry randomEntry(); /** - * Return a random keys (aka fields) from the hash stored at the bound key. If the provided {@code count} argument is - * positive, return a list of distinct keys, capped either at {@code count} or the hash size. If {@code count} is - * negative, the behavior changes and the command is allowed to return the same key multiple times. In this case, the - * number of returned keys is the absolute value of the specified count. + * Return a random keys from the hash stored at the bound key. If the provided {@code count} argument is positive, + * return a list of distinct keys, capped either at {@code count} or the hash size. If {@code count} is negative, the + * behavior changes and the command is allowed to return the same key multiple times. In this case, the number of + * returned keys is the absolute value of the specified count. * * @param count number of keys to return. * @return {@literal null} if key does not exist or when used in pipeline / transaction. @@ -159,84 +155,6 @@ public interface BoundHashOperations extends BoundKeyOperations { @Nullable Long lengthOfValue(HK hashKey); - default ExpireChanges expire(Expiration expiration, Collection hashKeys) { - return expire(expiration, FieldExpirationOptions.none(), hashKeys); - } - - ExpireChanges expire(Expiration expiration, FieldExpirationOptions options, Collection hashKeys); - - /** - * Set time to live for given {@code hashKey} (aka field). - * - * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); {@code -2} - * indicating there is no such field; {@literal null} when used in pipeline / transaction. - * @throws IllegalArgumentException if the timeout is {@literal null}. - * @see Redis Documentation: HEXPIRE - * @since 3.5 - */ - @Nullable - ExpireChanges expire(Duration timeout, Collection hashKeys); - - /** - * Set the expiration for given {@code hashKey} (aka field) as a {@literal date} timestamp. - * - * @param expireAt must not be {@literal null}. - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. - * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. - * @see Redis Documentation: HEXPIRE - * @since 3.5 - */ - @Nullable - ExpireChanges expireAt(Instant expireAt, Collection hashKeys); - - /** - * Remove the expiration from given {@code hashKey} (aka field). - * - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; - * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; {@literal null} when - * used in pipeline / transaction. - * @see Redis Documentation: HPERSIST - * @since 3.5 - */ - @Nullable - ExpireChanges persist(Collection hashKeys); - - /** - * Get the time to live for {@code hashKey} (aka field) in seconds. - * - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command - * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. - * @see Redis Documentation: HTTL - * @since 3.5 - */ - @Nullable - Expirations getExpire(Collection hashKeys); - - /** - * Get the time to live for {@code hashKey} (aka field) and convert it to the given {@link TimeUnit}. - * - * @param timeUnit must not be {@literal null}. - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command - * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. - * @see Redis Documentation: HTTL - * @since 3.5 - */ - @Nullable - Expirations getExpire(TimeUnit timeUnit, Collection hashKeys); - /** * Get size of hash at the bound key. * @@ -297,8 +215,45 @@ default ExpireChanges expire(Expiration expiration, Collection hashKeys) */ Cursor> scan(ScanOptions options); + /** + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at the + * bound {@code key}. Operations on the expiration object obtain keys at the time of invoking any expiration + * operation. + * + * @return the bound operations object to perform operations on the hash field expiration. + * @since 3.5 + */ + default BoundHashFieldExpirationOperations expiration() { + return new DefaultBoundHashFieldExpirationOperations<>(getOperations().opsForHash(), getKey(), this::keys); + } + + /** + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at the + * bound {@code key} for the given hash fields. + * + * @param hashFields collection of hash fields to operate on. + * @return the bound operations object to perform operations on the hash field expiration. + * @since 3.5 + */ + default BoundHashFieldExpirationOperations expiration(HK... hashFields) { + return expiration(Arrays.asList(hashFields)); + } + + /** + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at the + * bound {@code key} for the given hash fields. + * + * @param hashFields collection of hash fields to operate on. + * @return the bound operations object to perform operations on the hash field expiration. + * @since 3.5 + */ + default BoundHashFieldExpirationOperations expiration(Collection hashFields) { + return new DefaultBoundHashFieldExpirationOperations<>(getOperations().opsForHash(), getKey(), () -> hashFields); + } + /** * @return never {@literal null}. */ RedisOperations getOperations(); + } diff --git a/src/main/java/org/springframework/data/redis/core/DefaultBoundHashFieldExpirationOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultBoundHashFieldExpirationOperations.java new file mode 100644 index 0000000000..8dbabe6dd5 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/core/DefaultBoundHashFieldExpirationOperations.java @@ -0,0 +1,93 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.core; + +import java.time.Duration; +import java.time.Instant; +import java.util.Collection; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import org.springframework.data.redis.connection.Hash; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.core.types.Expirations; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +/** + * Default implementation of {@link BoundHashFieldExpirationOperations}. + * + * @author Mark Paluch + * @since 3.5 + */ +class DefaultBoundHashFieldExpirationOperations implements BoundHashFieldExpirationOperations { + + private final HashOperations operations; + private final H key; + private final Supplier> hashFields; + + public DefaultBoundHashFieldExpirationOperations(HashOperations operations, H key, + Supplier> hashFields) { + + this.operations = operations; + this.key = key; + this.hashFields = hashFields; + } + + @Override + public ExpireChanges expire(Expiration expiration, Hash.FieldExpirationOptions options) { + return operations.expire(key, expiration, options, getHashKeys()); + } + + @Nullable + @Override + public ExpireChanges expire(Duration timeout) { + return operations.expire(key, timeout, getHashKeys()); + } + + @Nullable + @Override + public ExpireChanges expireAt(Instant expireAt) { + return operations.expireAt(key, expireAt, getHashKeys()); + } + + @Nullable + @Override + public ExpireChanges persist() { + return operations.persist(key, getHashKeys()); + } + + @Nullable + @Override + public Expirations getTimeToLive() { + return operations.getTimeToLive(key, getHashKeys()); + } + + @Nullable + @Override + public Expirations getTimeToLive(TimeUnit timeUnit) { + return operations.getTimeToLive(key, timeUnit, getHashKeys()); + } + + private Collection getHashKeys() { + + Collection hks = hashFields.get(); + + Assert.state(hks != null, "Hash keys must not be null"); + return hks; + } + +} diff --git a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java index 2be7e0bd3f..804617616f 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java @@ -29,8 +29,9 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.convert.Converters; -import org.springframework.data.redis.core.Expirations.Timeouts; import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.core.types.Expirations; +import org.springframework.data.redis.core.types.Expirations.Timeouts; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -224,14 +225,11 @@ public ExpireChanges expire(K key, Duration duration, Collection hashKey byte[] rawKey = rawKey(key); byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); - boolean splitSecond = TimeoutUtils.hasMillis(duration); + boolean hasMillis = TimeoutUtils.hasMillis(duration); - List raw = execute(connection -> { - if (splitSecond) { - return connection.hashCommands().hpExpire(rawKey, duration.toMillis(), rawHashKeys); - } - return connection.hashCommands().hExpire(rawKey, TimeoutUtils.toSeconds(duration), rawHashKeys); - }); + List raw = execute(connection -> TimeoutUtils.hasMillis(duration) + ? connection.hashCommands().hpExpire(rawKey, duration.toMillis(), rawHashKeys) + : connection.hashCommands().hExpire(rawKey, TimeoutUtils.toSeconds(duration), rawHashKeys)); return raw != null ? ExpireChanges.of(orderedKeys, raw) : null; } @@ -243,8 +241,7 @@ public ExpireChanges expireAt(K key, Instant instant, Collection hashKey byte[] rawKey = rawKey(key); byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); - - Long millis = instant.toEpochMilli(); + long millis = instant.toEpochMilli(); List raw = execute(connection -> TimeoutUtils.containsSplitSecond(millis) ? connection.hashCommands().hpExpireAt(rawKey, millis, rawHashKeys) @@ -257,10 +254,10 @@ public ExpireChanges expireAt(K key, Instant instant, Collection hashKey public ExpireChanges expire(K key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys) { List orderedKeys = List.copyOf(hashKeys); - byte[] rawKey = rawKey(key); byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); - List raw = execute(connection -> connection.hashCommands().expireHashField(rawKey, expiration, options, rawHashKeys)); + List raw = execute( + connection -> connection.hashCommands().applyExpiration(rawKey, expiration, options, rawHashKeys)); return raw != null ? ExpireChanges.of(orderedKeys, raw) : null; } @@ -269,7 +266,6 @@ public ExpireChanges expire(K key, Expiration expiration, FieldExpirationOpt public ExpireChanges persist(K key, Collection hashKeys) { List orderedKeys = List.copyOf(hashKeys); - byte[] rawKey = rawKey(key); byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); @@ -279,7 +275,7 @@ public ExpireChanges persist(K key, Collection hashKeys) { } @Override - public Expirations getExpire(K key, TimeUnit timeUnit, Collection hashKeys) { + public Expirations getTimeToLive(K key, TimeUnit timeUnit, Collection hashKeys) { if(timeUnit.compareTo(TimeUnit.MILLISECONDS) < 0) { throw new IllegalArgumentException("%s precision is not supported must be >= MILLISECONDS".formatted(timeUnit)); diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java index d373a7f063..8c97c43fcd 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java @@ -15,10 +15,6 @@ */ package org.springframework.data.redis.core; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; -import org.springframework.data.redis.connection.ReactiveHashCommands.ExpireCommand; -import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; -import org.springframework.data.redis.core.types.Expiration; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -33,10 +29,16 @@ import java.util.function.Function; import org.reactivestreams.Publisher; + import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.ReactiveHashCommands; +import org.springframework.data.redis.connection.ReactiveHashCommands.ExpireCommand; +import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.connection.convert.Converters; -import org.springframework.data.redis.core.Expirations.Timeouts; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.core.types.Expirations; +import org.springframework.data.redis.core.types.Expirations.Timeouts; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -254,7 +256,9 @@ public Mono> expire(H key, Expiration expiration, FieldExpirat List rawHashKeys = orderedKeys.stream().map(this::rawHashKey).toList(); Mono> raw =createFlux(connection -> { - return connection.expireHashField(Mono.just(ExpireCommand.expire(rawHashKeys, expiration).from(rawKey).withOptions(options))).map(NumericResponse::getOutput); + return connection + .applyExpiration(Mono.just(ExpireCommand.expire(rawHashKeys, expiration).from(rawKey).withOptions(options))) + .map(NumericResponse::getOutput); }).collectList(); return raw.map(values -> ExpireChanges.of(orderedKeys, values)); @@ -288,7 +292,7 @@ public Mono> persist(H key, Collection hashKeys) { @Nullable @Override - public Mono> getExpire(H key, TimeUnit timeUnit, Collection hashKeys) { + public Mono> getTimeToLive(H key, TimeUnit timeUnit, Collection hashKeys) { if (timeUnit.compareTo(TimeUnit.MILLISECONDS) < 0) { throw new IllegalArgumentException("%s precision is not supported must be >= MILLISECONDS".formatted(timeUnit)); diff --git a/src/main/java/org/springframework/data/redis/core/ExpireChanges.java b/src/main/java/org/springframework/data/redis/core/ExpireChanges.java index b9486f639d..029922d96e 100644 --- a/src/main/java/org/springframework/data/redis/core/ExpireChanges.java +++ b/src/main/java/org/springframework/data/redis/core/ExpireChanges.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.core; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -23,6 +24,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; /** @@ -35,7 +37,7 @@ *
    • {@link #skipped()} returns keys for which the time to live has not been set because a precondition was not * met
    • * - * + * * @author Christoph Strobl * @since 3.5 */ @@ -50,23 +52,25 @@ public class ExpireChanges { /** * Factory Method to create {@link ExpireChanges} from raw sources. * - * @param keys the keys to associated with the raw values in states. Defines the actual order of entries within + * @param fields the fields to associated with the raw values in states. Defines the actual order of entries within * {@link ExpireChanges}. * @param states the raw Redis state change values. * @return new instance of {@link ExpireChanges}. * @param the key type used */ - public static ExpireChanges of(List keys, List states) { + public static ExpireChanges of(List fields, List states) { + + Assert.isTrue(fields.size() == states.size(), "Keys and States must have the same number of elements"); - if (keys.size() == 1) { - return new ExpireChanges<>(Map.of(keys.iterator().next(), stateFromValue(states.iterator().next()))); + if (fields.size() == 1) { + return new ExpireChanges<>(Map.of(fields.iterator().next(), stateFromValue(states.iterator().next()))); } - Map target = CollectionUtils.newLinkedHashMap(keys.size()); - for (int i = 0; i < keys.size(); i++) { - target.put(keys.get(i), stateFromValue(states.get(i))); + Map target = CollectionUtils.newLinkedHashMap(fields.size()); + for (int i = 0; i < fields.size(); i++) { + target.put(fields.get(i), stateFromValue(states.get(i))); } - return new ExpireChanges<>(target); + return new ExpireChanges<>(Collections.unmodifiableMap(target)); } /** diff --git a/src/main/java/org/springframework/data/redis/core/HashOperations.java b/src/main/java/org/springframework/data/redis/core/HashOperations.java index c32c33983c..67c4ce0fa8 100644 --- a/src/main/java/org/springframework/data/redis/core/HashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/HashOperations.java @@ -17,6 +17,7 @@ import java.time.Duration; import java.time.Instant; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; @@ -25,6 +26,7 @@ import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.core.types.Expirations; import org.springframework.lang.Nullable; /** @@ -96,7 +98,7 @@ public interface HashOperations { Double increment(H key, HK hashKey, double delta); /** - * Return a random hash key (aka field) from the hash stored at {@code key}. + * Return a random hash key from the hash stored at {@code key}. * * @param key must not be {@literal null}. * @return {@literal null} if key does not exist or when used in pipeline / transaction. @@ -118,10 +120,10 @@ public interface HashOperations { Map.Entry randomEntry(H key); /** - * Return random hash keys (aka fields) from the hash stored at {@code key}. If the provided {@code count} argument is - * positive, return a list of distinct hash keys, capped either at {@code count} or the hash size. If {@code count} is - * negative, the behavior changes and the command is allowed to return the same hash key multiple times. In this case, - * the number of returned fields is the absolute value of the specified count. + * Return random hash keys from the hash stored at {@code key}. If the provided {@code count} argument is positive, + * return a list of distinct hash keys, capped either at {@code count} or the hash size. If {@code count} is negative, + * the behavior changes and the command is allowed to return the same hash key multiple times. In this case, the + * number of returned fields is the absolute value of the specified count. * * @param key must not be {@literal null}. * @param count number of fields to return. @@ -228,15 +230,16 @@ public interface HashOperations { Cursor> scan(H key, ScanOptions options); /** - * Set time to live for given {@code hashKey} (aka field). + * Set time to live for given {@code hashKey} . * * @param key must not be {@literal null}. * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); {@code -2} - * indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time + * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition + * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / + * transaction. * @throws IllegalArgumentException if the timeout is {@literal null}. * @see Redis Documentation: HEXPIRE * @since 3.5 @@ -245,15 +248,16 @@ public interface HashOperations { ExpireChanges expire(H key, Duration timeout, Collection hashKeys); /** - * Set the expiration for given {@code hashKey} (aka field) as a {@literal date} timestamp. + * Set the expiration for given {@code hashKeys} as a {@literal date} timestamp. * * @param key must not be {@literal null}. * @param expireAt must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. * @see Redis Documentation: HEXPIRE * @since 3.5 @@ -261,16 +265,33 @@ public interface HashOperations { @Nullable ExpireChanges expireAt(H key, Instant expireAt, Collection hashKeys); + /** + * Apply the expiration for given {@code hashKeys} as a {@literal date} timestamp. + * + * @param key must not be {@literal null}. + * @param expiration must not be {@literal null}. + * @param options must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is + * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating + * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | + * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in + * pipeline / transaction. + * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ + @Nullable ExpireChanges expire(H key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys); /** - * Remove the expiration from given {@code hashKey} (aka field). + * Remove the expiration from given {@code hashKeys} . * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; - * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; {@literal null} when - * used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is + * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPERSIST * @since 3.5 */ @@ -278,38 +299,77 @@ public interface HashOperations { ExpireChanges persist(H key, Collection hashKeys); /** - * Get the time to live for {@code hashKey} (aka field) in seconds. + * Get the time to live for {@code hashKeys} in seconds. * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command - * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative + * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration + * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HTTL * @since 3.5 */ @Nullable - default Expirations getExpire(H key, Collection hashKeys) { - return getExpire(key, TimeUnit.SECONDS, hashKeys); + default Expirations getTimeToLive(H key, Collection hashKeys) { + return getTimeToLive(key, TimeUnit.SECONDS, hashKeys); } /** - * Get the time to live for {@code hashKey} (aka field) and convert it to the given {@link TimeUnit}. + * Get the time to live for {@code hashKeys} and convert it to the given {@link TimeUnit}. * * @param key must not be {@literal null}. * @param timeUnit must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command - * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. + * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative + * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration + * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / + * transaction. * @see Redis Documentation: HTTL * @since 3.5 */ @Nullable - Expirations getExpire(H key, TimeUnit timeUnit, Collection hashKeys); + Expirations getTimeToLive(H key, TimeUnit timeUnit, Collection hashKeys); + + /** + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at + * {@code key}. Operations on the expiration object obtain keys at the time of invoking any expiration operation. + * + * @param key must not be {@literal null}. + * @return the bound operations object to perform operations on the hash field expiration. + * @since 3.5 + */ + default BoundHashFieldExpirationOperations expiration(H key) { + return new DefaultBoundHashFieldExpirationOperations<>(this, key, () -> keys(key)); + } + + /** + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at + * {@code key} for the given hash fields. + * + * @param hashFields collection of hash fields to operate on. + * @return the bound operations object to perform operations on the hash field expiration. + * @since 3.5 + */ + default BoundHashFieldExpirationOperations expiration(H key, HK... hashFields) { + return expiration(key, Arrays.asList(hashFields)); + } + + /** + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at + * {@code key} for the given hash fields. + * + * @param hashFields collection of hash fields to operate on. + * @return the bound operations object to perform operations on the hash field expiration. + * @since 3.5 + */ + default BoundHashFieldExpirationOperations expiration(H key, Collection hashFields) { + return new DefaultBoundHashFieldExpirationOperations<>(this, key, () -> hashFields); + } /** * @return never {@literal null}. */ RedisOperations getOperations(); + } diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java index 2d0cbcaf10..757dbcea76 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java @@ -15,8 +15,6 @@ */ package org.springframework.data.redis.core; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; -import org.springframework.data.redis.core.types.Expiration; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -28,6 +26,9 @@ import java.util.Map; import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.core.types.Expirations; import org.springframework.lang.Nullable; /** @@ -102,7 +103,7 @@ public interface ReactiveHashOperations { Mono increment(H key, HK hashKey, double delta); /** - * Return a random hash key (aka field) from the hash stored at {@code key}. + * Return a random hash key from the hash stored at {@code key}. * * @param key must not be {@literal null}. * @return @@ -122,10 +123,10 @@ public interface ReactiveHashOperations { Mono> randomEntry(H key); /** - * Return random hash keys (aka fields) from the hash stored at {@code key}. If the provided {@code count} argument is - * positive, return a list of distinct hash keys, capped either at {@code count} or the hash size. If {@code count} is - * negative, the behavior changes and the command is allowed to return the same hash key multiple times. In this case, - * the number of returned fields is the absolute value of the specified count. + * Return random hash keys from the hash stored at {@code key}. If the provided {@code count} argument is positive, + * return a list of distinct hash keys, capped either at {@code count} or the hash size. If {@code count} is negative, + * the behavior changes and the command is allowed to return the same hash key multiple times. In this case, the + * number of returned fields is the absolute value of the specified count. * * @param key must not be {@literal null}. * @param count number of fields to return. @@ -242,7 +243,7 @@ default Flux> scan(H key) { Mono> expire(H key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys); /** - * Set the expiration for given {@code hashKey} (aka field) as a {@literal date} timestamp. + * Set the expiration for given {@code hashKey} as a {@literal date} timestamp. * * @param key must not be {@literal null}. * @param expireAt must not be {@literal null}. @@ -260,7 +261,7 @@ default Flux> scan(H key) { Mono> expireAt(H key, Instant expireAt, Collection hashKeys); /** - * Remove the expiration from given {@code hashKey} (aka field). + * Remove the expiration from given {@code hashKey} . * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. @@ -274,7 +275,7 @@ default Flux> scan(H key) { Mono> persist(H key, Collection hashKeys); /** - * Get the time to live for {@code hashKey} (aka field) in seconds. + * Get the time to live for {@code hashKey} in seconds. * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. @@ -286,12 +287,12 @@ default Flux> scan(H key) { * @since 3.5 */ @Nullable - default Mono> getExpire(H key, Collection hashKeys) { - return getExpire(key, TimeUnit.SECONDS, hashKeys); + default Mono> getTimeToLive(H key, Collection hashKeys) { + return getTimeToLive(key, TimeUnit.SECONDS, hashKeys); } /** - * Get the time to live for {@code hashKey} (aka field) and convert it to the given {@link TimeUnit}. + * Get the time to live for {@code hashKey} and convert it to the given {@link TimeUnit}. * * @param key must not be {@literal null}. * @param timeUnit must not be {@literal null}. @@ -304,7 +305,7 @@ default Mono> getExpire(H key, Collection hashKeys) { * @since 3.5 */ @Nullable - Mono> getExpire(H key, TimeUnit timeUnit, Collection hashKeys); + Mono> getTimeToLive(H key, TimeUnit timeUnit, Collection hashKeys); /** * Removes the given {@literal key}. diff --git a/src/main/java/org/springframework/data/redis/core/RedisCommand.java b/src/main/java/org/springframework/data/redis/core/RedisCommand.java index e9303233df..53dc940a97 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisCommand.java +++ b/src/main/java/org/springframework/data/redis/core/RedisCommand.java @@ -107,6 +107,13 @@ public enum RedisCommand { HSET("w", 3, 3), // HSETNX("w", 3, 3), // HVALS("r", 1, 1), // + HEXPIRE("w", 5), // + HEXPIREAT("w", 5), // + HPEXPIRE("w", 5), // + HPEXPIREAT("w", 5), // + HPERSIST("w", 4), // + HTTL("r", 4), // + HPTTL("r", 4), // // -- I INCR("rw", 1), // INCRBYFLOAT("rw", 2, 2), // diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java index 8c1ad67ad6..def0dca04b 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java @@ -376,6 +376,7 @@ default Boolean expireAt(K key, Instant expireAt) { @Nullable Boolean persist(K key); + // TODO: Add TimeToLive (getTimeToLive) /** * Get the time to live for {@code key} in seconds. * diff --git a/src/main/java/org/springframework/data/redis/core/Expirations.java b/src/main/java/org/springframework/data/redis/core/types/Expirations.java similarity index 62% rename from src/main/java/org/springframework/data/redis/core/Expirations.java rename to src/main/java/org/springframework/data/redis/core/types/Expirations.java index 958f90e3a8..7101cb60fa 100644 --- a/src/main/java/org/springframework/data/redis/core/Expirations.java +++ b/src/main/java/org/springframework/data/redis/core/types/Expirations.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.redis.core; +package org.springframework.data.redis.core.types; import java.time.Duration; +import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -29,31 +30,32 @@ import org.springframework.util.ObjectUtils; /** - * Value Object linking a number of keys to their {@link Expiration} retaining the order of the original source. + * Value Object linking a number of keys to their {@link TimeToLive} retaining the order of the original source. * Dedicated higher level methods interpret raw expiration values retrieved from a Redis Client. *
        *
      1. {@link #persistent()} returns keys that do not have an associated time to live
      2. *
      3. {@link #missing()} returns keys that do not exist and therefore have no associated time to live
      4. - *
      5. {@link #expirations()} returns the ordered list of {@link Expiration expirations} based on the raw values
      6. + *
      7. {@link #ttl()} returns the ordered list of {@link TimeToLive expirations} based on the raw values
      8. *
      9. {@link #expiring()} returns the expiring keys along with their {@link Duration time to live}
      10. *
      - * + * * @author Christoph Strobl + * @author Mark Paluch * @since 3.5 */ -public class Expirations { // TODO: should we move this to let's say Hash.class or another place +public class Expirations { private final TimeUnit unit; - private final Map expirations; + private final Map expirations; - Expirations(TimeUnit unit, Map expirations) { + Expirations(TimeUnit unit, Map expirations) { this.unit = unit; this.expirations = expirations; } /** * Factory Method to create {@link Expirations} from raw sources provided in a given {@link TimeUnit}. - * + * * @param targetUnit the actual time unit of the raw timeToLive values. * @param keys the keys to associated with the raw values in timeToLive. Defines the actual order of entries within * {@link Expirations}. @@ -69,12 +71,12 @@ public static Expirations of(TimeUnit targetUnit, List keys, Timeouts } if (keys.size() == 1) { return new Expirations<>(targetUnit, - Map.of(keys.iterator().next(), Expiration.of(timeouts.raw().iterator().next(), timeouts.timeUnit()))); + Map.of(keys.iterator().next(), TimeToLive.of(timeouts.raw().iterator().next(), timeouts.timeUnit()))); } - Map target = CollectionUtils.newLinkedHashMap(keys.size()); + Map target = CollectionUtils.newLinkedHashMap(keys.size()); for (int i = 0; i < keys.size(); i++) { - target.put(keys.get(i), Expiration.of(timeouts.get(i), timeouts.timeUnit())); + target.put(keys.get(i), TimeToLive.of(timeouts.get(i), timeouts.timeUnit())); } return new Expirations<>(targetUnit, target); } @@ -83,87 +85,98 @@ public static Expirations of(TimeUnit targetUnit, List keys, Timeouts * @return an ordered set of keys that do not have a time to live. */ public Set persistent() { - return filterByState(Expiration.PERSISTENT); + return filterByState(TimeToLive.PERSISTENT); } /** - * @return an ordered set of keys that do not exists and therefore do not have a time to live. + * @return an ordered set of keys that do not exist and therefore do not have a time to live. */ public Set missing() { - return filterByState(Expiration.MISSING); + return filterByState(TimeToLive.MISSING); } /** - * @return an ordered set of all {@link Expirations expirations} where the {@link Expiration#value()} is using the - * {@link TimeUnit} defined in {@link #precision()}. + * @return an ordered set of all {@link Expirations expirations} where the {@link TimeToLive#value()} is using the + * {@link TimeUnit} defined in {@link #timeUnit()}. */ - public List expirations() { + public List ttl() { return expirations.values().stream().map(it -> it.convert(this.unit)).toList(); } /** - * @return the {@link TimeUnit} for {@link Expiration expirations} held by this instance. + * @return the {@link TimeUnit} for {@link TimeToLive expirations} held by this instance. */ - public TimeUnit precision() { + public TimeUnit timeUnit() { return unit; } /** * @return an ordered {@link List} of {@link java.util.Map.Entry entries} combining keys with their actual time to - * live. {@link Expiration#isMissing() Missing} and {@link Expiration#isPersistent() persistent} entries are + * live. {@link TimeToLive#isMissing() Missing} and {@link TimeToLive#isPersistent() persistent} entries are * skipped. */ public List> expiring() { + return expirations.entrySet().stream().filter(it -> !it.getValue().isMissing() && !it.getValue().isPersistent()) .map(it -> Map.entry(it.getKey(), toDuration(it.getValue()))).toList(); } + /** + * @return the ordered collection of keys that are associated with an expiration. + */ + public Collection keys() { + return expirations.keySet(); + } + /** * @param key - * @return the {@link Expirations expirations} where the {@link Expiration#value()} is using the {@link TimeUnit} - * defined in {@link #precision()} or {@literal null} if no entry could be found. + * @return the {@link Expirations expirations} where the {@link TimeToLive#value()} is using the {@link TimeUnit} + * defined in {@link #timeUnit()} or {@literal null} if no entry could be found. */ @Nullable - public Expiration expirationOf(K key) { + public TimeToLive expirationOf(K key) { + + TimeToLive timeToLive = expirations.get(key); - Expiration expiration = expirations.get(key); - if (expiration == null) { + if (timeToLive == null) { return null; } - return expiration.convert(this.unit); + return timeToLive.convert(this.unit); } /** * @param key * @return the time to live value of the requested key if it exists and the expiration is neither - * {@link Expiration#isMissing() missing} nor {@link Expiration#isPersistent() persistent}, {@literal null} + * {@link TimeToLive#isMissing() missing} nor {@link TimeToLive#isPersistent() persistent}, {@literal null} * otherwise. */ @Nullable public Duration ttlOf(K key) { - - Expiration expiration = expirationOf(key); - if (expiration == null) { - return null; - } - return toDuration(expiration); + return toDuration(expirationOf(key)); } - private Set filterByState(Expiration filter) { + private Set filterByState(TimeToLive filter) { return expirations.entrySet().stream().filter(entry -> entry.getValue().equals(filter)).map(Map.Entry::getKey) .collect(Collectors.toCollection(LinkedHashSet::new)); } @Nullable - static Duration toDuration(Expiration expiration) { + static Duration toDuration(@Nullable TimeToLive timeToLive) { - if (expiration.sourceUnit == null) { + if (timeToLive == null || timeToLive.sourceUnit == null) { return null; } - return Duration.of(expiration.raw(), expiration.sourceUnit.toChronoUnit()); + + return Duration.of(timeToLive.raw(), timeToLive.sourceUnit.toChronoUnit()); } + /** + * Collection of timeouts associated with a {@link TimeUnit}. + * + * @param timeUnit + * @param raw + */ public record Timeouts(TimeUnit timeUnit, List raw) { Long get(int index) { @@ -173,6 +186,7 @@ Long get(int index) { public int size() { return raw.size(); } + } /** @@ -182,30 +196,57 @@ public int size() { * {@link #PERSISTENT} mark predefined states returned by Redis indicating a time to live value could not be retrieved * due to various reasons. */ - public static class Expiration { // TODO: is Expiry a better name for this type? + public static class TimeToLive { + /** + * Predefined {@link TimeToLive} for a key that does not exist and therefore does not have a time to live. + */ + public static TimeToLive MISSING = new TimeToLive(-2L); + + /** + * Predefined {@link TimeToLive} for a key that exists but does not expire. + */ + public static TimeToLive PERSISTENT = new TimeToLive(-1L); + + private final @Nullable TimeUnit sourceUnit; + private final @Nullable TimeUnit targetUnit; private final long raw; - @Nullable TimeUnit sourceUnit; - @Nullable TimeUnit targetUnit; - public Expiration(long value) { + TimeToLive(long value) { this(value, null); } - public Expiration(long value, @Nullable TimeUnit sourceUnit) { + TimeToLive(long value, @Nullable TimeUnit sourceUnit) { this(value, sourceUnit, null); } - public Expiration(long value, @Nullable TimeUnit sourceUnit, @Nullable TimeUnit targetUnit) { + TimeToLive(long value, @Nullable TimeUnit sourceUnit, @Nullable TimeUnit targetUnit) { this.raw = value; this.sourceUnit = sourceUnit; this.targetUnit = targetUnit; } + /** + * Factory method for creating {@link TimeToLive} instances, returning predefined ones if the value matches a known + * reserved state. + * + * @param value the TTL value. + * @param timeUnit time unit for the given value. + * @return the {@link TimeToLive} for the given raw value. + */ + public static TimeToLive of(Number value, TimeUnit timeUnit) { + + return switch (value.intValue()) { + case -2 -> MISSING; + case -1 -> PERSISTENT; + default -> new TimeToLive(value.longValue(), timeUnit); + }; + } + /** * The raw source value as returned by the Redis Client. * - * @return the raw data + * @return the raw data. */ public long raw() { return raw; @@ -219,58 +260,36 @@ public long value() { if (sourceUnit == null || targetUnit == null) { return raw; } + return targetUnit.convert(raw, sourceUnit); } /** * @param timeUnit must not be {@literal null}. - * @return the {@link Expiration} instance with new target {@link TimeUnit} set for obtaining the {@link #value() + * @return the {@link TimeToLive} instance with new target {@link TimeUnit} set for obtaining the {@link #value() * value}, or the same instance raw value cannot or must not be converted. */ - public Expiration convert(TimeUnit timeUnit) { + public TimeToLive convert(TimeUnit timeUnit) { if (sourceUnit == null || ObjectUtils.nullSafeEquals(sourceUnit, timeUnit)) { return this; } - return new Expiration(raw, sourceUnit, timeUnit); - } - - /** - * Predefined {@link Expiration} for a key that does not exists and therefore does not have a time to live. - */ - public static Expiration MISSING = new Expiration(-2L); - /** - * Predefined {@link Expiration} for a key that exists but does not expire. - */ - public static Expiration PERSISTENT = new Expiration(-1L); + return new TimeToLive(raw, sourceUnit, timeUnit); + } /** * @return {@literal true} if key exists but does not expire. */ public boolean isPersistent() { - return PERSISTENT.equals(this); + return PERSISTENT.raw() == raw(); } /** - * @return {@literal true} if key does not exists and therefore does not have a time to live. + * @return {@literal true} if key does not exist and therefore does not have a time to live. */ public boolean isMissing() { - return MISSING.equals(this); - } - - /** - * Factory method for creating {@link Expiration} instances, returning predefined ones if the value matches a known - * reserved state. - * - * @return the {@link Expiration} for the given raw value. - */ - static Expiration of(Number value, TimeUnit timeUnit) { - return switch (value.intValue()) { - case -2 -> MISSING; - case -1 -> PERSISTENT; - default -> new Expiration(value.longValue(), timeUnit); - }; + return MISSING.raw() == raw(); } @Override @@ -280,7 +299,7 @@ public boolean equals(Object o) { return true; } - if (!(o instanceof Expiration that)) { + if (!(o instanceof Expirations.TimeToLive that)) { return false; } @@ -299,5 +318,16 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(raw); } + + @Override + public String toString() { + + return switch ((int) raw()) { + case -2 -> "MISSING"; + case -1 -> "PERSISTENT"; + default -> "%d %s".formatted(raw(), sourceUnit); + }; + } } + } diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java index ad22195ad0..18c3c24e20 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java @@ -15,19 +15,15 @@ */ package org.springframework.data.redis.support.collections; -import java.time.Duration; -import java.time.Instant; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.connection.DataType; -import org.springframework.data.redis.core.Expirations; -import org.springframework.data.redis.core.ExpireChanges; +import org.springframework.data.redis.core.BoundHashFieldExpirationOperations; import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisOperations; @@ -328,28 +324,13 @@ public Cursor> scan() { } @Override - public ExpireChanges expire(Duration timeout, Collection hashKeys) { - return Objects.requireNonNull(hashOps.expire(timeout, hashKeys)); + public BoundHashFieldExpirationOperations expiration() { + return hashOps.expiration(); } @Override - public ExpireChanges expireAt(Instant expireAt, Collection hashKeys) { - return Objects.requireNonNull(hashOps.expireAt(expireAt, hashKeys)); - } - - @Override - public ExpireChanges persist(Collection hashKeys) { - return Objects.requireNonNull(hashOps.persist(hashKeys)); - } - - @Override - public Expirations getExpire(Collection hashKeys) { - return Objects.requireNonNull(hashOps.getExpire(hashKeys)); - } - - @Override - public Expirations getExpire(TimeUnit timeUnit, Collection hashKeys) { - return Objects.requireNonNull(hashOps.getExpire(timeUnit, hashKeys)); + public BoundHashFieldExpirationOperations expiration(Collection hashFields) { + return hashOps.expiration(hashFields); } private void checkResult(@Nullable Object obj) { diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java index 54d002d549..955942eb25 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java @@ -15,16 +15,13 @@ */ package org.springframework.data.redis.support.collections; -import java.time.Duration; -import java.time.Instant; +import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; -import org.springframework.data.redis.core.Expirations; -import org.springframework.data.redis.core.ExpireChanges; +import org.springframework.data.redis.core.BoundHashFieldExpirationOperations; import org.springframework.lang.Nullable; /** @@ -33,6 +30,7 @@ * @author Costin Leau * @author Christoph Strobl * @author Tihomi Mateev + * @author Mark Paluch */ public interface RedisMap extends RedisStore, ConcurrentMap { @@ -71,7 +69,7 @@ public interface RedisMap extends RedisStore, ConcurrentMap { * @since 2.6 */ @Nullable - Map.Entry randomEntry(); + Map.Entry randomEntry(); /** * @since 1.4 @@ -80,69 +78,34 @@ public interface RedisMap extends RedisStore, ConcurrentMap { Iterator> scan(); /** - * Set time to live for given {hash {@code key}. + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at + * {@code key}. Operations on the expiration object obtain keys at the time of invoking any expiration operation. * - * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time is set/updated; - * {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); {@code -2} - * indicating there is no such field; {@literal null} when used in pipeline / transaction. - * @throws IllegalArgumentException if the timeout is {@literal null}. - * @see Redis Documentation: HEXPIRE + * @return the bound operations object to perform operations on the hash field expiration. * @since 3.5 */ - ExpireChanges expire(Duration timeout, Collection hashKeys); + BoundHashFieldExpirationOperations expiration(); /** - * Set the expiration for given hash {@code key} as a {@literal date} timestamp. + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at the + * bound {@code key} for the given hash fields. * - * @param expireAt must not be {@literal null}. - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is deleted - * already due to expiration, or provided expiry interval is in the past; {@code 1} indicating expiration time is - * set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition is not met); - * {@code -2} indicating there is no such field; {@literal null} when used in pipeline / transaction. - * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. - * @see Redis Documentation: HEXPIRE + * @param hashFields collection of hash fields to operate on. + * @return the bound operations object to perform operations on the hash field expiration. * @since 3.5 */ - ExpireChanges expireAt(Instant expireAt, Collection hashKeys); + default BoundHashFieldExpirationOperations expiration(K... hashFields) { + return expiration(Arrays.asList(hashFields)); + } /** - * Remove the expiration from given hash {@code key}. + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at the + * bound {@code key} for the given hash fields. * - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is removed; - * {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such field; {@literal null} when - * used in pipeline / transaction. - * @see Redis Documentation: HPERSIST + * @param hashFields collection of hash fields to operate on. + * @return the bound operations object to perform operations on the hash field expiration. * @since 3.5 */ - ExpireChanges persist(Collection hashKeys); + BoundHashFieldExpirationOperations expiration(Collection hashFields); - /** - * Get the time to live for hash {@code key} in seconds. - * - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command - * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. - * @see Redis Documentation: HTTL - * @since 3.5 - */ - Expirations getExpire(Collection hashKeys); - - /** - * Get the time to live for hash {@code key} and convert it to the given {@link TimeUnit}. - * - * @param timeUnit must not be {@literal null}. - * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative value - * to signal an error. The command returns {@code -1} if the key exists but has no associated expiration time. The command - * returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / transaction. - * @see Redis Documentation: HTTL - * @since 3.5 - */ - Expirations getExpire(TimeUnit timeUnit, Collection hashKeys); } diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java index 54d7f0c9d3..c6a5f0a45d 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java @@ -17,15 +17,20 @@ import java.io.IOException; import java.io.OutputStream; -import java.time.Duration; -import java.time.Instant; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.connection.DataType; -import org.springframework.data.redis.core.Expirations; -import org.springframework.data.redis.core.ExpireChanges; +import org.springframework.data.redis.core.BoundHashFieldExpirationOperations; import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.RedisOperations; import org.springframework.lang.Nullable; @@ -298,42 +303,17 @@ public synchronized void storeToXML(OutputStream os, String comment) throws IOEx @Override public Iterator> scan() { - throw new UnsupportedOperationException(); - } - - @Override - public ExpireChanges expire(Duration timeout, Collection hashKeys) { - - Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return (ExpireChanges) hashOps.expire(timeout, keys); + return (Iterator) delegate.scan(); } @Override - public ExpireChanges expireAt(Instant expireAt, Collection hashKeys) { - - Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return (ExpireChanges) hashOps.expireAt(expireAt, keys); + public BoundHashFieldExpirationOperations expiration() { + return (BoundHashFieldExpirationOperations) delegate.expiration(); } @Override - public ExpireChanges persist(Collection hashKeys) { - - Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return (ExpireChanges) hashOps.persist(keys); - } - - @Override - public Expirations getExpire(Collection hashKeys) { - - Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return (Expirations) hashOps.getExpire(keys); - } - - @Override - public Expirations getExpire(TimeUnit timeUnit, Collection hashKeys) { - - Collection keys = hashKeys.stream().map(key -> (String) key).toList(); - return (Expirations) hashOps.getExpire(timeUnit, keys); + public BoundHashFieldExpirationOperations expiration(Collection hashFields) { + return (BoundHashFieldExpirationOperations) delegate.expiration((Collection) hashFields); } } diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index ffa5bcd100..94b8db7657 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -98,6 +98,7 @@ import org.springframework.data.redis.test.condition.RedisDriver; import org.springframework.data.redis.test.util.HexStringUtils; import org.springframework.data.util.Streamable; +import org.springframework.util.ObjectUtils; /** * Base test class for AbstractConnection integration tests @@ -752,6 +753,45 @@ void testExecute() { assertThat(stringSerializer.deserialize((byte[]) getResults().get(1))).isEqualTo("bar"); } + @Test + @EnabledOnCommand("HEXPIRE") + void testExecuteHashFieldExpiration() { + + actual.add(connection.hSet("foo", "bar", "field")); + actual.add(connection.execute("HTTL", "foo", "FIELDS", "1", "bar")); + actual.add(connection.execute("HEXPIRE", "foo", "100", "NX", "FIELDS", "1", "bar")); + actual.add(connection.execute("HPERSIST", "foo", "FIELDS", "1", "bar")); + actual.add(connection.execute("HTTL", "foo", "FIELDS", "1", "bar")); + + List results = getResults(); + + assertThat(deserializeList(results, 1, stringSerializer)).containsOnly(-1L); + assertThat(deserializeList(results, 2, stringSerializer)).containsOnly(1L); + assertThat(deserializeList(results, 3, stringSerializer)).containsOnly(1L); + assertThat(deserializeList(results, 4, stringSerializer)).containsOnly(-1L); + } + + List deserializeList(List objects, int index, RedisSerializer serializer) { + + List result = new ArrayList<>(); + Object o = objects.get(index); + if (o instanceof List ls) { + for (Object obj : ls) { + + if (obj instanceof byte[]) { + result.add(serializer.deserialize((byte[]) obj)); + } else { + result.add(obj); + } + } + + return result; + } + + throw new IllegalArgumentException( + "Object at index " + index + " is not a list but " + ObjectUtils.nullSafeToString(o)); + } + @Test void testExecuteNoArgs() { @@ -3436,7 +3476,7 @@ void hStrLenReturnsZeroWhenKeyDoesNotExist() { @Test @EnabledOnCommand("HEXPIRE") public void hExpireReturnsSuccessAndSetsTTL() { - + actual.add(connection.hSet("hash-hexpire", "key-2", "value-2")); actual.add(connection.hExpire("hash-hexpire", 5L, "key-2")); actual.add(connection.hTtl("hash-hexpire", "key-2")); diff --git a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java index 4abd23dacf..bbb84a76de 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java @@ -15,9 +15,8 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; import java.io.IOException; import java.time.Duration; @@ -30,15 +29,15 @@ import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; + import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.StringObjectFactory; import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension; -import org.springframework.data.redis.core.Expirations.Expiration; import org.springframework.data.redis.core.ExpireChanges.ExpiryChangeState; +import org.springframework.data.redis.core.types.Expirations.TimeToLive; import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.extension.RedisStanalone; import org.springframework.data.redis.test.extension.parametrized.MethodSource; @@ -228,11 +227,11 @@ void testExpireAndGetExpireMillis() { assertThat(redisTemplate.opsForHash().expire(key, Duration.ofMillis(500), List.of(key1))) .satisfies(ExpireChanges::allOk); - assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1))).satisfies(expirations -> { + assertThat(redisTemplate.opsForHash().getTimeToLive(key, List.of(key1))).satisfies(expirations -> { assertThat(expirations.missing()).isEmpty(); - assertThat(expirations.precision()).isEqualTo(TimeUnit.SECONDS); - assertThat(expirations.expirationOf(key1)).extracting(Expiration::raw, InstanceOfAssertFactories.LONG) + assertThat(expirations.timeUnit()).isEqualTo(TimeUnit.SECONDS); + assertThat(expirations.expirationOf(key1)).extracting(TimeToLive::raw, InstanceOfAssertFactories.LONG) .isBetween(0L, 1L); assertThat(expirations.ttlOf(key1)).isBetween(Duration.ZERO, Duration.ofSeconds(1)); }); @@ -259,16 +258,48 @@ void testExpireAndGetExpireSeconds() { assertThat(changes.stateChanges()).map(ExpiryChangeState::value).containsExactly(1L, 1L); }); - assertThat(redisTemplate.opsForHash().getExpire(key, TimeUnit.SECONDS, List.of(key1, key2))) + assertThat(redisTemplate.opsForHash().getTimeToLive(key, TimeUnit.SECONDS, List.of(key1, key2))) .satisfies(expirations -> { assertThat(expirations.missing()).isEmpty(); - assertThat(expirations.precision()).isEqualTo(TimeUnit.SECONDS); - assertThat(expirations.expirationOf(key1)).extracting(Expiration::raw, InstanceOfAssertFactories.LONG) + assertThat(expirations.timeUnit()).isEqualTo(TimeUnit.SECONDS); + assertThat(expirations.expirationOf(key1)).extracting(TimeToLive::raw, InstanceOfAssertFactories.LONG) .isBetween(0L, 5L); assertThat(expirations.ttlOf(key1)).isBetween(Duration.ofSeconds(1), Duration.ofSeconds(5)); }); } + @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") + void testBoundExpireAndGetExpireSeconds() { + + K key = keyFactory.instance(); + HK key1 = hashKeyFactory.instance(); + HV val1 = hashValueFactory.instance(); + HK key2 = hashKeyFactory.instance(); + HV val2 = hashValueFactory.instance(); + hashOps.put(key, key1, val1); + hashOps.put(key, key2, val2); + + BoundHashOperations hashOps = redisTemplate.boundHashOps(key); + BoundHashFieldExpirationOperations exp = hashOps.expiration(key1, key2); + + assertThat(exp.expire(Duration.ofSeconds(5))).satisfies(changes -> { + assertThat(changes.allOk()).isTrue(); + assertThat(changes.stateOf(key1)).isEqualTo(ExpiryChangeState.OK); + assertThat(changes.ok()).containsExactlyInAnyOrder(key1, key2); + assertThat(changes.missed()).isEmpty(); + assertThat(changes.stateChanges()).map(ExpiryChangeState::value).containsExactly(1L, 1L); + }); + + assertThat(exp.getTimeToLive(TimeUnit.SECONDS)).satisfies(expirations -> { + assertThat(expirations.missing()).isEmpty(); + assertThat(expirations.timeUnit()).isEqualTo(TimeUnit.SECONDS); + assertThat(expirations.expirationOf(key1)).extracting(TimeToLive::raw, InstanceOfAssertFactories.LONG) + .isBetween(0L, 5L); + assertThat(expirations.ttlOf(key1)).isBetween(Duration.ofSeconds(1), Duration.ofSeconds(5)); + }); + } + @ParameterizedRedisTest @EnabledOnCommand("HEXPIRE") void testExpireAtAndGetExpireMillis() { @@ -284,27 +315,29 @@ void testExpireAtAndGetExpireMillis() { assertThat(redisTemplate.opsForHash().expireAt(key, Instant.now().plusMillis(500), List.of(key1, key2))) .satisfies(ExpireChanges::allOk); - assertThat(redisTemplate.opsForHash().getExpire(key, TimeUnit.MILLISECONDS, List.of(key1, key2))) + assertThat(redisTemplate.opsForHash().getTimeToLive(key, TimeUnit.MILLISECONDS, List.of(key1, key2))) .satisfies(expirations -> { assertThat(expirations.missing()).isEmpty(); - assertThat(expirations.precision()).isEqualTo(TimeUnit.MILLISECONDS); - assertThat(expirations.expirationOf(key1)).extracting(Expiration::raw, InstanceOfAssertFactories.LONG) + assertThat(expirations.timeUnit()).isEqualTo(TimeUnit.MILLISECONDS); + assertThat(expirations.expirationOf(key1)).extracting(TimeToLive::raw, InstanceOfAssertFactories.LONG) .isBetween(0L, 500L); assertThat(expirations.ttlOf(key1)).isBetween(Duration.ZERO, Duration.ofMillis(500)); }); } @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void expireThrowsErrorOfNanoPrecision() { K key = keyFactory.instance(); HK key1 = hashKeyFactory.instance(); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> redisTemplate.opsForHash().getExpire(key, TimeUnit.NANOSECONDS, List.of(key1))); + .isThrownBy(() -> redisTemplate.opsForHash().getTimeToLive(key, TimeUnit.NANOSECONDS, List.of(key1))); } @ParameterizedRedisTest + @EnabledOnCommand("HEXPIRE") void testExpireWithOptionsNone() { K key = keyFactory.instance(); @@ -360,7 +393,7 @@ void testPersistAndGetExpireMillis() { assertThat(redisTemplate.opsForHash().persist(key, List.of(key2))).satisfies(ExpireChanges::allOk); - assertThat(redisTemplate.opsForHash().getExpire(key, List.of(key1, key2))).satisfies(expirations -> { + assertThat(redisTemplate.opsForHash().getTimeToLive(key, List.of(key1, key2))).satisfies(expirations -> { assertThat(expirations.expirationOf(key1).isPersistent()).isFalse(); assertThat(expirations.expirationOf(key2).isPersistent()).isTrue(); }); diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java index a128a29293..2d574ee123 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java @@ -15,11 +15,10 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; -import static org.junit.jupiter.api.condition.OS.MAC; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; +import static org.junit.jupiter.api.condition.OS.*; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import reactor.test.StepVerifier; import java.time.Duration; @@ -34,10 +33,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.condition.DisabledOnOs; + import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.SettingsUtils; import org.springframework.data.redis.StringObjectFactory; +import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.convert.Converters; @@ -523,7 +524,7 @@ void testExpireAndGetExpireMillis() { assertThat(changes.allOk()).isTrue(); }).verifyComplete(); - hashOperations.getExpire(key, List.of(key1)) // + hashOperations.getTimeToLive(key, List.of(key1)) // .as(StepVerifier::create) // .assertNext(it -> { assertThat(it.expirationOf(key1).raw()).isBetween(0L, 2L); @@ -576,7 +577,7 @@ void testExpireAndGetExpireSeconds() { assertThat(changes.allOk()).isTrue(); }).verifyComplete(); - hashOperations.getExpire(key, TimeUnit.SECONDS, List.of(key1, key2)) // + hashOperations.getTimeToLive(key, TimeUnit.SECONDS, List.of(key1, key2)) // .as(StepVerifier::create) // .assertNext(it -> { assertThat(it.expirationOf(key1).raw()).isBetween(0L, 5L); @@ -603,7 +604,7 @@ void testExpireAtAndGetExpireMillis() { assertThat(changes.allOk()).isTrue(); }).verifyComplete(); - redisTemplate.opsForHash().getExpire(key, List.of(key1, key2)).as(StepVerifier::create)// + redisTemplate.opsForHash().getTimeToLive(key, List.of(key1, key2)).as(StepVerifier::create)// .assertNext(it -> { assertThat(it.expirationOf(key1).raw()).isBetween(0L, 2L); assertThat(it.expirationOf(key2).raw()).isBetween(0L, 2L); @@ -633,7 +634,7 @@ void testPersistAndGetExpireMillis() { assertThat(changes.allOk()).isTrue(); }).verifyComplete(); - redisTemplate.opsForHash().getExpire(key, List.of(key1, key2)).as(StepVerifier::create)// + redisTemplate.opsForHash().getTimeToLive(key, List.of(key1, key2)).as(StepVerifier::create)// .assertNext(expirations -> { assertThat(expirations.persistent()).contains(key1, key2); }).verifyComplete(); diff --git a/src/test/java/org/springframework/data/redis/core/ExpirationsUnitTest.java b/src/test/java/org/springframework/data/redis/core/types/ExpirationsUnitTest.java similarity index 80% rename from src/test/java/org/springframework/data/redis/core/ExpirationsUnitTest.java rename to src/test/java/org/springframework/data/redis/core/types/ExpirationsUnitTest.java index 5fc1953d3d..db5ba98605 100644 --- a/src/test/java/org/springframework/data/redis/core/ExpirationsUnitTest.java +++ b/src/test/java/org/springframework/data/redis/core/types/ExpirationsUnitTest.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.redis.core; +package org.springframework.data.redis.core.types; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import java.time.Duration; import java.util.List; @@ -26,11 +26,14 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.springframework.data.redis.core.Expirations.Timeouts; + +import org.springframework.data.redis.core.types.Expirations.Timeouts; /** + * Unit test for {@link Expirations} + * * @author Christoph Strobl - * @since 2025/02 + * @author Mark Paluch */ class ExpirationsUnitTest { @@ -38,19 +41,19 @@ class ExpirationsUnitTest { static final String KEY_2 = "key-2"; static final String KEY_3 = "key-3"; - @ParameterizedTest + @ParameterizedTest // GH-3054 @EnumSource(TimeUnit.class) void expirationMemorizesSourceUnit(TimeUnit targetUnit) { Expirations exp = Expirations.of(targetUnit, List.of(KEY_1), new Timeouts(TimeUnit.SECONDS, List.of(120L))); - assertThat(exp.expirations().get(0)).satisfies(expiration -> { + assertThat(exp.ttl().get(0)).satisfies(expiration -> { assertThat(expiration.raw()).isEqualTo(120L); assertThat(expiration.value()).isEqualTo(targetUnit.convert(120, TimeUnit.SECONDS)); }); } - @Test + @Test // GH-3054 void expirationsCategorizesElements() { Expirations exp = createExpirations(new Timeouts(TimeUnit.SECONDS, List.of(-2L, -1L, 120L))); @@ -60,7 +63,7 @@ void expirationsCategorizesElements() { assertThat(exp.expiring()).containsExactly(Map.entry(KEY_3, Duration.ofMinutes(2))); } - @Test + @Test // GH-3054 void returnsNullForMissingElements() { Expirations exp = createExpirations(new Timeouts(TimeUnit.SECONDS, List.of(-2L, -1L, 120L))); @@ -69,7 +72,7 @@ void returnsNullForMissingElements() { assertThat(exp.ttlOf("missing")).isNull(); } - @Test + @Test // GH-3054 void ttlReturnsDurationForEntriesWithTimeout() { Expirations exp = createExpirations(new Timeouts(TimeUnit.SECONDS, List.of(-2L, -1L, 120L))); @@ -77,7 +80,7 @@ void ttlReturnsDurationForEntriesWithTimeout() { assertThat(exp.ttlOf(KEY_3)).isEqualTo(Duration.ofMinutes(2)); } - @Test + @Test // GH-3054 void ttlReturnsNullForPersistentAndMissingEntries() { Expirations exp = createExpirations(new Timeouts(TimeUnit.SECONDS, List.of(-2L, -1L, 120L))); @@ -86,6 +89,14 @@ void ttlReturnsNullForPersistentAndMissingEntries() { assertThat(exp.ttlOf(KEY_2)).isNull(); } + @Test // GH-3054 + void shouldRenderToString() { + + assertThat(Expirations.TimeToLive.PERSISTENT).hasToString("PERSISTENT"); + assertThat(Expirations.TimeToLive.MISSING).hasToString("MISSING"); + assertThat(Expirations.TimeToLive.of(1, TimeUnit.SECONDS)).hasToString("1 SECONDS"); + } + static Expirations createExpirations(Timeouts timeouts) { List keys = IntStream.range(1, timeouts.raw().size() + 1).mapToObj("key-%s"::formatted).toList(); diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java index 5ce8e54418..0a03b7340e 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java @@ -15,9 +15,8 @@ */ package org.springframework.data.redis.support.collections; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assumptions.assumeThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.*; +import static org.assertj.core.api.Assumptions.*; import java.io.IOException; import java.text.DecimalFormat; @@ -36,14 +35,16 @@ import org.assertj.core.api.Assumptions; import org.junit.jupiter.api.BeforeEach; + import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.DoubleAsStringObjectFactory; import org.springframework.data.redis.LongAsStringObjectFactory; import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.RedisSystemException; -import org.springframework.data.redis.core.ExpireChanges; +import org.springframework.data.redis.core.BoundHashFieldExpirationOperations; import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.ExpireChanges; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; @@ -204,15 +205,15 @@ void testExpire() { V v1 = getValue(); assertThat(map.put(k1, v1)).isEqualTo(null); - Collection keys = Collections.singletonList(k1); - assertThat(map.expire(Duration.ofSeconds(5), keys)).satisfies(ExpireChanges::allOk); - assertThat(map.getExpire(keys)).satisfies(expiration -> { + BoundHashFieldExpirationOperations ops = map.expiration(Collections.singletonList(k1)); + assertThat(ops.expire(Duration.ofSeconds(5))).satisfies(ExpireChanges::allOk); + assertThat(ops.getTimeToLive()).satisfies(expiration -> { assertThat(expiration.expirationOf(k1).raw()).isBetween(1L, 5L); }); - assertThat(map.getExpire(TimeUnit.MILLISECONDS, keys)).satisfies(expiration -> { + assertThat(ops.getTimeToLive(TimeUnit.MILLISECONDS)).satisfies(expiration -> { assertThat(expiration.expirationOf(k1).raw()).isBetween(1000L, 5000L); }); - assertThat(map.persist(keys)).satisfies(ExpireChanges::allOk); + assertThat(ops.persist()).satisfies(ExpireChanges::allOk); } @ParameterizedRedisTest @@ -223,15 +224,15 @@ void testExpireAt() { V v1 = getValue(); assertThat(map.put(k1, v1)).isEqualTo(null); - Collection keys = Collections.singletonList(k1); - assertThat(map.expireAt(Instant.now().plusSeconds(5), keys)).satisfies(ExpireChanges::allOk); - assertThat(map.getExpire(keys)).satisfies(expiration -> { + BoundHashFieldExpirationOperations ops = map.expiration(Collections.singletonList(k1)); + assertThat(ops.expireAt(Instant.now().plusSeconds(5))).satisfies(ExpireChanges::allOk); + assertThat(ops.getTimeToLive()).satisfies(expiration -> { assertThat(expiration.expirationOf(k1).raw()).isBetween(1L, 5L); }); - assertThat(map.getExpire(TimeUnit.MILLISECONDS, keys)).satisfies(expiration -> { + assertThat(ops.getTimeToLive(TimeUnit.MILLISECONDS)).satisfies(expiration -> { assertThat(expiration.expirationOf(k1).raw()).isBetween(1000L, 5000L); }); - assertThat(map.persist(keys)).satisfies(ExpireChanges::allOk); + assertThat(ops.persist()).satisfies(ExpireChanges::allOk); } @ParameterizedRedisTest diff --git a/src/test/java/org/springframework/data/redis/support/collections/RedisPropertiesIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/RedisPropertiesIntegrationTests.java index 17e63c3113..be3e627a28 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/RedisPropertiesIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/RedisPropertiesIntegrationTests.java @@ -185,12 +185,6 @@ void testStringPropertyNames() throws Exception { assertThat(keys.contains(key3)).isTrue(); } - @ParameterizedRedisTest - @Override - public void testScanWorksCorrectly() { - assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> super.testScanWorksCorrectly()); - } - // DATAREDIS-241 public static Collection testParams() { From 06f35914c5c190a3b53cfe471abdede2ab17cbc7 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 5 Mar 2025 11:59:20 +0100 Subject: [PATCH 140/187] Polishing. Update javadoc and format sources. See #3054. --- .../connection/ReactiveHashCommands.java | 38 +++++ .../redis/connection/RedisHashCommands.java | 55 ++++--- .../connection/StringRedisConnection.java | 20 +-- .../BoundHashFieldExpirationOperations.java | 44 ++---- .../core/DefaultReactiveHashOperations.java | 6 +- .../data/redis/core/HashOperations.java | 32 +--- .../redis/core/ReactiveHashOperations.java | 43 ++++-- .../data/redis/core/RedisOperations.java | 1 - .../AbstractConnectionIntegrationTests.java | 96 +++++++----- .../jedis/JedisClusterConnectionTests.java | 137 ++++++++++++------ .../LettuceClusterConnectionTests.java | 105 ++++++++++---- ...eReactiveHashCommandsIntegrationTests.java | 48 ++---- ...DefaultHashOperationsIntegrationTests.java | 35 +++-- ...eactiveHashOperationsIntegrationTests.java | 56 +++---- .../AbstractRedisMapIntegrationTests.java | 10 +- 15 files changed, 432 insertions(+), 294 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java index 1e9fd94f3a..c463737747 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java @@ -865,22 +865,52 @@ private ExpireCommand(@Nullable ByteBuffer key, List fields, Expirat this.options = options; } + /** + * Creates a new {@link ExpireCommand}. + * + * @param fields the {@code field} names to apply expiration to + * @param timeout the actual timeout + * @param unit the unit of measure for the {@code timeout}. + * @return new instance of {@link ExpireCommand}. + */ public static ExpireCommand expire(List fields, long timeout, TimeUnit unit) { Assert.notNull(fields, "Field must not be null"); return expire(fields, Expiration.from(timeout, unit)); } + /** + * Creates a new {@link ExpireCommand}. + * + * @param fields the {@code field} names to apply expiration to. + * @param ttl the actual timeout. + * @return new instance of {@link ExpireCommand}. + */ public static ExpireCommand expire(List fields, Duration ttl) { Assert.notNull(fields, "Field must not be null"); return expire(fields, Expiration.from(ttl)); } + /** + * Creates a new {@link ExpireCommand}. + * + * @param fields the {@code field} names to apply expiration to + * @param expiration the {@link Expiration} to apply to the given {@literal fields}. + * @return new instance of {@link ExpireCommand}. + */ public static ExpireCommand expire(List fields, Expiration expiration) { return new ExpireCommand(null, fields, expiration, FieldExpirationOptions.none()); } + /** + * Creates a new {@link ExpireCommand}. + * + * @param fields the {@code field} names to apply expiration to + * @param ttl the unix point in time when to expire the given {@literal fields}. + * @param precision can be {@link TimeUnit#SECONDS} or {@link TimeUnit#MILLISECONDS}. + * @return new instance of {@link ExpireCommand}. + */ public static ExpireCommand expireAt(List fields, Instant ttl, TimeUnit precision) { if (precision.compareTo(TimeUnit.MILLISECONDS) > 0) { @@ -890,10 +920,18 @@ public static ExpireCommand expireAt(List fields, Instant ttl, TimeU return expire(fields, Expiration.unixTimestamp(ttl.toEpochMilli(), TimeUnit.MILLISECONDS)); } + /** + * @param key the {@literal key} from which to expire the {@literal fields} from. + * @return new instance of {@link ExpireCommand}. + */ public ExpireCommand from(ByteBuffer key) { return new ExpireCommand(key, getFields(), expiration, options); } + /** + * @param options additional options to be sent along with the command. + * @return new instance of {@link ExpireCommand}. + */ public ExpireCommand withOptions(FieldExpirationOptions options) { return new ExpireCommand(getKey(), getFields(), getExpiration(), options); } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java index f2d736b8ef..d038708526 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java @@ -255,11 +255,34 @@ public interface RedisHashCommands { @Nullable Long hStrLen(byte[] key, byte[] field); + /** + * Apply a given {@link org.springframework.data.redis.core.types.Expiration} to the given {@literal fields}. + * + * @param key must not be {@literal null}. + * @param expiration the {@link org.springframework.data.redis.core.types.Expiration} to apply. + * @param fields the names of the {@literal fields} to apply the {@literal expiration} to. + * @return a {@link List} holding the command result for each field in order - {@code 2} indicating the specific field + * is deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration + * time is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating there is no + * such field; + * @since 3.5 + */ default @Nullable List applyExpiration(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, byte[]... fields) { return applyExpiration(key, expiration, FieldExpirationOptions.none(), fields); } + /** + * @param key must not be {@literal null}. + * @param expiration the {@link org.springframework.data.redis.core.types.Expiration} to apply. + * @param options additional options to be sent along with the command. + * @param fields the names of the {@literal fields} to apply the {@literal expiration} to. + * @return a {@link List} holding the command result for each field in order - {@code 2} indicating the specific field + * is deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration + * time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT + * condition is not met); {@code -2} indicating there is no such field; + * @since 3.5 + */ @Nullable default List applyExpiration(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, FieldExpirationOptions options, byte[]... fields) { @@ -304,9 +327,8 @@ default List applyExpiration(byte[] key, org.springframework.data.redis.co * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time - * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition - * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / - * transaction. + * is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HEXPIRE * @since 3.5 */ @@ -324,9 +346,8 @@ default List hExpire(byte[] key, long seconds, byte[]... fields) { * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time - * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition - * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / - * transaction. + * is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HEXPIRE * @since 3.5 */ @@ -362,9 +383,8 @@ default List hExpire(byte[] key, Duration ttl, byte[]... fields) { * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time - * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition - * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / - * transaction. + * is set/updated; {@code 0} indicating the expiration time is not set ; {@code -2} indicating there is no + * such field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPEXPIRE * @since 3.5 */ @@ -382,9 +402,8 @@ default List hpExpire(byte[] key, long millis, byte[]... fields) { * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time - * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition - * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / - * transaction. + * is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPEXPIRE * @since 3.5 */ @@ -420,9 +439,8 @@ default List hpExpire(byte[] key, Duration ttl, byte[]... fields) { * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating - * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | - * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in - * pipeline / transaction. + * expiration time is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating + * there is no such field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HEXPIREAT * @since 3.5 */ @@ -457,9 +475,8 @@ default List hExpireAt(byte[] key, long unixTime, byte[]... fields) { * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating - * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | - * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in - * pipeline / transaction. + * expiration time is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating + * there is no such field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPEXPIREAT * @since 3.5 */ @@ -531,8 +548,6 @@ List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions. * @since 3.5 */ @Nullable - // TODO: this is complete nonsense as it would jeopardize negative values - // TODO: this should be a List> List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields); /** diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java index 5ff0e9946f..1069e430c8 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -2341,9 +2341,8 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time - * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition - * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / - * transaction. + * is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HEXPIRE * @since 3.5 */ @@ -2377,9 +2376,8 @@ default List hExpire(String key, long seconds, String... fields) { * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time - * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition - * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / - * transaction. + * is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating there is no such + * field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPEXPIRE * @since 3.5 */ @@ -2413,9 +2411,8 @@ default List hpExpire(String key, long millis, String... fields) { * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating - * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | - * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in - * pipeline / transaction. + * expiration time is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating + * there is no such field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HEXPIREAT * @since 3.5 */ @@ -2449,9 +2446,8 @@ default List hExpireAt(String key, long unixTime, String... fields) { * @param fields must not be {@literal null}. * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating - * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | - * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in - * pipeline / transaction. + * expiration time is set/updated; {@code 0} indicating the expiration time is not set; {@code -2} indicating + * there is no such field; {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPEXPIREAT * @since 3.5 */ diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java index 33e0ff82e6..d49041a66d 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java @@ -34,33 +34,30 @@ public interface BoundHashFieldExpirationOperations { /** - * Apply {@link Expiration} to the hash without any additional constraints. + * Apply {@link Expiration} to the bound hash key/hash fields without any additional constraints. * * @param expiration the expiration definition. - * @return changes to the hash fields. + * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. */ default ExpireChanges expire(Expiration expiration) { return expire(expiration, Hash.FieldExpirationOptions.none()); } /** - * Apply {@link Expiration} to the hash fields given {@link Hash.FieldExpirationOptions expiration options}. + * Apply {@link Expiration} to the bound hash key/hash fields given {@link Hash.FieldExpirationOptions expiration + * options}. * * @param expiration the expiration definition. * @param options expiration options. - * @return changes to the hash fields. + * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. */ ExpireChanges expire(Expiration expiration, Hash.FieldExpirationOptions options); /** - * Set time to live for given {@code hashKey}. + * Set time to live for the bound hash key/hash fields. * * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is - * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time - * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition - * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / - * transaction. + * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. * @throws IllegalArgumentException if the timeout is {@literal null}. * @see Redis Documentation: HEXPIRE * @since 3.5 @@ -69,14 +66,10 @@ default ExpireChanges expire(Expiration expiration) { ExpireChanges expire(Duration timeout); /** - * Set the expiration for given {@code hashKey} as a {@literal date} timestamp. + * Set the expiration for the bound hash key/hash fields as a {@literal date} timestamp. * * @param expireAt must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is - * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating - * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | - * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in - * pipeline / transaction. + * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. * @see Redis Documentation: HEXPIRE * @since 3.5 @@ -85,11 +78,9 @@ default ExpireChanges expire(Expiration expiration) { ExpireChanges expireAt(Instant expireAt); /** - * Remove the expiration from given {@code hashKey} . + * Remove the expiration from the bound hash key/hash fields. * - * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is - * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such - * field; {@literal null} when used in pipeline / transaction. + * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPERSIST * @since 3.5 */ @@ -97,12 +88,9 @@ default ExpireChanges expire(Expiration expiration) { ExpireChanges persist(); /** - * Get the time to live for {@code hashKey} in seconds. + * Get the time to live for bound hash key/hash fields in seconds. * - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative - * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration - * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / - * transaction. + * @return the actual expirations in seconds for the hash fields. {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HTTL * @since 3.5 */ @@ -110,12 +98,10 @@ default ExpireChanges expire(Expiration expiration) { Expirations getTimeToLive(); /** - * Get the time to live for {@code hashKey} and convert it to the given {@link TimeUnit}. + * Get the time to live for the bound hash key/hash fields and convert it to the given {@link TimeUnit}. * * @param timeUnit must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative - * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration - * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / + * @return the actual expirations for the hash fields in the given time unit. {@literal null} when used in pipeline / * transaction. * @see Redis Documentation: HTTL * @since 3.5 diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java index 8c97c43fcd..540b351778 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java @@ -29,7 +29,6 @@ import java.util.function.Function; import org.reactivestreams.Publisher; - import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.ReactiveHashCommands; @@ -249,13 +248,14 @@ public Mono> expire(H key, Duration timeout, Collection ha } @Override - public Mono> expire(H key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys) { + public Mono> expire(H key, Expiration expiration, FieldExpirationOptions options, + Collection hashKeys) { List orderedKeys = List.copyOf(hashKeys); ByteBuffer rawKey = rawKey(key); List rawHashKeys = orderedKeys.stream().map(this::rawHashKey).toList(); - Mono> raw =createFlux(connection -> { + Mono> raw = createFlux(connection -> { return connection .applyExpiration(Mono.just(ExpireCommand.expire(rawHashKeys, expiration).from(rawKey).withOptions(options))) .map(NumericResponse::getOutput); diff --git a/src/main/java/org/springframework/data/redis/core/HashOperations.java b/src/main/java/org/springframework/data/redis/core/HashOperations.java index 67c4ce0fa8..f57143c737 100644 --- a/src/main/java/org/springframework/data/redis/core/HashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/HashOperations.java @@ -235,11 +235,7 @@ public interface HashOperations { * @param key must not be {@literal null}. * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is - * deleted already due to expiration, or provided expiry interval is 0; {@code 1} indicating expiration time - * is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | GT | LT condition - * is not met); {@code -2} indicating there is no such field; {@literal null} when used in pipeline / - * transaction. + * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. * @throws IllegalArgumentException if the timeout is {@literal null}. * @see Redis Documentation: HEXPIRE * @since 3.5 @@ -253,11 +249,7 @@ public interface HashOperations { * @param key must not be {@literal null}. * @param expireAt must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is - * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating - * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | - * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in - * pipeline / transaction. + * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. * @see Redis Documentation: HEXPIRE * @since 3.5 @@ -272,11 +264,7 @@ public interface HashOperations { * @param expiration must not be {@literal null}. * @param options must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is - * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating - * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | - * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in - * pipeline / transaction. + * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. * @see Redis Documentation: HEXPIRE * @since 3.5 @@ -289,9 +277,7 @@ public interface HashOperations { * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is - * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such - * field; {@literal null} when used in pipeline / transaction. + * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HPERSIST * @since 3.5 */ @@ -303,10 +289,7 @@ public interface HashOperations { * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative - * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration - * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / - * transaction. + * @return the actual expirations in seconds for the hash fields. {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HTTL * @since 3.5 */ @@ -321,10 +304,7 @@ default Expirations getTimeToLive(H key, Collection hashKeys) { * @param key must not be {@literal null}. * @param timeUnit must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative - * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration - * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / - * transaction. + * @return the actual expirations for the hash fields. {@literal null} when used in pipeline / transaction. * @see Redis Documentation: HTTL * @since 3.5 */ diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java index 757dbcea76..2fe3b0c65b 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java @@ -238,8 +238,31 @@ default Flux> scan(H key) { */ Flux> scan(H key, ScanOptions options); + /** + * Set time to live for given {@literal hashKeys} stored within {@literal key}. + * + * @param key must not be {@literal null}. + * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. + * @param hashKeys must not be {@literal null}. + * @return a {@link Mono} emitting changes to the hash fields. + * @throws IllegalArgumentException if the timeout is {@literal null}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ Mono> expire(H key, Duration timeout, Collection hashKeys); + /** + * Set time to live for given {@literal hashKeys} stored within {@literal key}. + * + * @param key must not be {@literal null}. + * @param expiration must not be {@literal null}. + * @param options additional options to apply. + * @param hashKeys must not be {@literal null}. + * @return a {@link Mono} emitting changes to the hash fields. + * @throws IllegalArgumentException if the timeout is {@literal null}. + * @see Redis Documentation: HEXPIRE + * @since 3.5 + */ Mono> expire(H key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys); /** @@ -248,11 +271,7 @@ default Flux> scan(H key) { * @param key must not be {@literal null}. * @param expireAt must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 2} indicating the specific field is - * deleted already due to expiration, or provided expiry interval is in the past; {@code 1} indicating - * expiration time is set/updated; {@code 0} indicating the expiration time is not set (a provided NX | XX | - * GT | LT condition is not met); {@code -2} indicating there is no such field; {@literal null} when used in - * pipeline / transaction. + * @return a {@link Mono} emitting changes to the hash fields. * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. * @see Redis Documentation: HEXPIRE * @since 3.5 @@ -265,9 +284,7 @@ default Flux> scan(H key) { * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: {@code 1} indicating expiration time is - * removed; {@code -1} field has no expiration time to be removed; {@code -2} indicating there is no such - * field; {@literal null} when used in pipeline / transaction. + * @return a {@link Mono} emitting changes to the hash fields. * @see Redis Documentation: HPERSIST * @since 3.5 */ @@ -279,10 +296,7 @@ default Flux> scan(H key) { * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative - * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration - * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / - * transaction. + * @return a {@link Mono} emitting {@link Expirations} of the hash fields. * @see Redis Documentation: HTTL * @since 3.5 */ @@ -297,10 +311,7 @@ default Mono> getTimeToLive(H key, Collection hashKeys) { * @param key must not be {@literal null}. * @param timeUnit must not be {@literal null}. * @param hashKeys must not be {@literal null}. - * @return a list of {@link Long} values for each of the fields provided: the time to live in seconds; or a negative - * value to signal an error. The command returns {@code -1} if the key exists but has no associated expiration - * time. The command returns {@code -2} if the key does not exist; {@literal null} when used in pipeline / - * transaction. + * @return a {@link Mono} emitting {@link Expirations} of the hash fields. * @see Redis Documentation: HTTL * @since 3.5 */ diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java index def0dca04b..8c1ad67ad6 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java @@ -376,7 +376,6 @@ default Boolean expireAt(K key, Instant expireAt) { @Nullable Boolean persist(K key); - // TODO: Add TimeToLive (getTimeToLive) /** * Get the time to live for {@code key} in seconds. * diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index 94b8db7657..573f105247 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -15,24 +15,48 @@ */ package org.springframework.data.redis.connection; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; -import static org.awaitility.Awaitility.*; -import static org.junit.jupiter.api.condition.OS.*; -import static org.springframework.data.redis.connection.BitFieldSubCommands.*; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.*; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.*; -import static org.springframework.data.redis.connection.ClusterTestVariables.*; -import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.*; -import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.*; -import static org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs.*; -import static org.springframework.data.redis.core.ScanOptions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.fail; +import static org.assertj.core.api.Assertions.within; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.condition.OS.MAC; +import static org.springframework.data.redis.connection.BitFieldSubCommands.create; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.FAIL; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.INT_8; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.signed; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.unsigned; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_1; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_2; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_3; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_1; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_2; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_3; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_4; +import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.KILOMETERS; +import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs; +import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.newGeoSearchArgs; +import static org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs.newGeoSearchStoreArgs; +import static org.springframework.data.redis.core.ScanOptions.scanOptions; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; @@ -753,7 +777,7 @@ void testExecute() { assertThat(stringSerializer.deserialize((byte[]) getResults().get(1))).isEqualTo("bar"); } - @Test + @Test // GH- @EnabledOnCommand("HEXPIRE") void testExecuteHashFieldExpiration() { @@ -3473,7 +3497,7 @@ void hStrLenReturnsZeroWhenKeyDoesNotExist() { verifyResults(Arrays.asList(new Object[] { 0L })); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireReturnsSuccessAndSetsTTL() { @@ -3484,10 +3508,10 @@ public void hExpireReturnsSuccessAndSetsTTL() { List results = getResults(); assertThat(results.get(0)).isEqualTo(Boolean.TRUE); assertThat((List) results.get(1)).contains(1L); - assertThat((List) results.get(2)).allSatisfy( value -> assertThat((Long)value).isBetween(0L, 5L)); + assertThat((List) results.get(2)).allSatisfy(value -> assertThat((Long) value).isBetween(0L, 5L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { @@ -3498,7 +3522,7 @@ public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireReturnsTwoWhenZeroProvided() { @@ -3508,7 +3532,7 @@ public void hExpireReturnsTwoWhenZeroProvided() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(2L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HPEXPIRE") public void hpExpireReturnsSuccessAndSetsTTL() { @@ -3519,10 +3543,10 @@ public void hpExpireReturnsSuccessAndSetsTTL() { List results = getResults(); assertThat(results.get(0)).isEqualTo(Boolean.TRUE); assertThat((List) results.get(1)).contains(1L); - assertThat((List) results.get(2)).allSatisfy( value -> assertThat((Long)value).isBetween(0L, 5000L)); + assertThat((List) results.get(2)).allSatisfy(value -> assertThat((Long) value).isBetween(0L, 5000L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HPEXPIRE") public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { @@ -3533,7 +3557,7 @@ public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HPEXPIRE") public void hpExpireReturnsTwoWhenZeroProvided() { @@ -3543,7 +3567,7 @@ public void hpExpireReturnsTwoWhenZeroProvided() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(2L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIREAT") public void hExpireAtReturnsSuccessAndSetsTTL() { @@ -3556,10 +3580,10 @@ public void hExpireAtReturnsSuccessAndSetsTTL() { List results = getResults(); assertThat(results.get(0)).isEqualTo(Boolean.TRUE); assertThat((List) results.get(1)).contains(1L); - assertThat((List) results.get(2)).allSatisfy( value -> assertThat((Long)value).isBetween(0L, 5L)); + assertThat((List) results.get(2)).allSatisfy(value -> assertThat((Long) value).isBetween(0L, 5L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIREAT") public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { @@ -3572,7 +3596,7 @@ public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIREAT") public void hExpireAtReturnsTwoWhenZeroProvided() { @@ -3584,7 +3608,7 @@ public void hExpireAtReturnsTwoWhenZeroProvided() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(2L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIREAT") public void hpExpireAtReturnsSuccessAndSetsTTL() { @@ -3597,10 +3621,10 @@ public void hpExpireAtReturnsSuccessAndSetsTTL() { List results = getResults(); assertThat(results.get(0)).isEqualTo(Boolean.TRUE); assertThat((List) results.get(1)).contains(1L); - assertThat((List) results.get(2)).allSatisfy( value -> assertThat((Long)value).isBetween(0L, 5L)); + assertThat((List) results.get(2)).allSatisfy(value -> assertThat((Long) value).isBetween(0L, 5L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIREAT") public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { @@ -3613,7 +3637,7 @@ public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HPEXPIREAT") public void hpExpireAdReturnsTwoWhenZeroProvided() { @@ -3625,7 +3649,7 @@ public void hpExpireAdReturnsTwoWhenZeroProvided() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(2L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HPERSIST") public void hPersistReturnsSuccessAndPersistsField() { @@ -3637,7 +3661,7 @@ public void hPersistReturnsSuccessAndPersistsField() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(1L), List.of(1L), List.of(-1L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HPERSIST") public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { @@ -3647,7 +3671,7 @@ public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(-1L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HPERSIST") public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { @@ -3658,7 +3682,7 @@ public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(-2L), List.of(-2L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HTTL") public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { @@ -3668,7 +3692,7 @@ public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(-1L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HTTL") public void hTtlReturnsMinusIndependendOfTimeUnitOneWhenFieldHasNoExpiration() { @@ -3678,7 +3702,7 @@ public void hTtlReturnsMinusIndependendOfTimeUnitOneWhenFieldHasNoExpiration() { verifyResults(Arrays.asList(Boolean.TRUE, List.of(-1L))); } - @Test + @Test // GH-3054 @EnabledOnCommand("HTTL") public void hTtlReturnsMinusTwoWhenFieldOrKeyMissing() { diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 4e41e60954..4bed88c0a5 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -15,17 +15,37 @@ */ package org.springframework.data.redis.connection.jedis; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.data.Offset.offset; -import static org.springframework.data.redis.connection.BitFieldSubCommands.*; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.*; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.*; -import static org.springframework.data.redis.connection.ClusterTestVariables.*; -import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.*; -import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.*; -import static org.springframework.data.redis.connection.RedisListCommands.*; -import static org.springframework.data.redis.connection.RedisZSetCommands.*; -import static org.springframework.data.redis.core.ScanOptions.*; +import static org.springframework.data.redis.connection.BitFieldSubCommands.create; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.FAIL; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.INT_8; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.signed; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.unsigned; +import static org.springframework.data.redis.connection.ClusterTestVariables.CLUSTER_HOST; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_1; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_2; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_3; +import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_1_PORT; +import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_2_PORT; +import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_3_PORT; +import static org.springframework.data.redis.connection.ClusterTestVariables.REPLICAOF_NODE_1_PORT; +import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_1; +import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_2; +import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_3; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_1; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_2; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_3; +import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.KILOMETERS; +import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs; +import static org.springframework.data.redis.connection.RedisListCommands.Direction; +import static org.springframework.data.redis.connection.RedisListCommands.Position; +import static org.springframework.data.redis.connection.RedisZSetCommands.Range; +import static org.springframework.data.redis.core.ScanOptions.NONE; +import static org.springframework.data.redis.core.ScanOptions.scanOptions; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.HostAndPort; @@ -37,14 +57,23 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Range.Bound; @@ -1040,9 +1069,10 @@ public void hStrLenReturnsZeroWhenKeyDoesNotExist() { assertThat(clusterConnection.hashCommands().hStrLen(KEY_1_BYTES, KEY_1_BYTES)).isEqualTo(0L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); @@ -1050,9 +1080,10 @@ public void hExpireReturnsSuccessAndSetsTTL() { .allSatisfy(val -> assertThat(val).isBetween(0L, 5L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); // missing field assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_1_BYTES)).contains(-2L); @@ -1060,27 +1091,30 @@ public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { assertThat(clusterConnection.hashCommands().hExpire(KEY_2_BYTES, 5L, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 5000L, KEY_2_BYTES)).contains(1L); - assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS,KEY_2_BYTES)) + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS, KEY_2_BYTES)) .allSatisfy(val -> assertThat(val).isBetween(0L, 5000L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); // missing field assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 5L, KEY_1_BYTES)).contains(-2L); @@ -1088,26 +1122,31 @@ public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { assertThat(clusterConnection.hashCommands().hpExpire(KEY_2_BYTES, 5L, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireAtReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); - assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)).allSatisfy(val -> assertThat(val).isBetween(0L, 5L)); + assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)) + .allSatisfy(val -> assertThat(val).isBetween(0L, 5L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); @@ -1117,27 +1156,31 @@ public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { assertThat(clusterConnection.hashCommands().hExpireAt(KEY_2_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireAdReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireAtReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, TimeUnit.MILLISECONDS, KEY_2_BYTES)) .allSatisfy(val -> assertThat(val).isBetween(0L, 5000L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); @@ -1147,55 +1190,60 @@ public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_2_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireAdReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hPersistReturnsSuccessAndPersistsField() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); + assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); - assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(1L); assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); - assertThat(clusterConnection.hashCommands().hPersist(KEY_3_BYTES,KEY_2_BYTES)).contains(-2L); + assertThat(clusterConnection.hashCommands().hPersist(KEY_3_BYTES, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hTtlReturnsMinusTwoWhenFieldOrKeyMissing() { assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); - assertThat(clusterConnection.hashCommands().hTtl(KEY_3_BYTES,KEY_2_BYTES)).contains(-2L); - + assertThat(clusterConnection.hashCommands().hTtl(KEY_3_BYTES, KEY_2_BYTES)).contains(-2L); } @Test // DATAREDIS-315 @@ -1346,7 +1394,7 @@ public void blMoveShouldMoveElementsCorrectly() { .isEqualTo(VALUE_2_BYTES); assertThat( clusterConnection.bLMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, Direction.RIGHT, Direction.LEFT, 0.01)) - .isNull(); + .isNull(); assertThat(nativeConnection.lrange(SAME_SLOT_KEY_1, 0, -1)).isEmpty(); assertThat(nativeConnection.lrange(SAME_SLOT_KEY_2, 0, -1)).containsExactly(VALUE_2, VALUE_3); @@ -2946,13 +2994,13 @@ void bitFieldIncrByWithOverflowShouldWorkCorrectly() { assertThat(clusterConnection.stringCommands().bitField(JedisConverters.toBytes(KEY_1), create().incr(unsigned(2)).valueAt(BitFieldSubCommands.Offset.offset(102L)).overflow(FAIL).by(1L))) - .containsExactly(1L); + .containsExactly(1L); assertThat(clusterConnection.stringCommands().bitField(JedisConverters.toBytes(KEY_1), create().incr(unsigned(2)).valueAt(BitFieldSubCommands.Offset.offset(102L)).overflow(FAIL).by(1L))) - .containsExactly(2L); + .containsExactly(2L); assertThat(clusterConnection.stringCommands().bitField(JedisConverters.toBytes(KEY_1), create().incr(unsigned(2)).valueAt(BitFieldSubCommands.Offset.offset(102L)).overflow(FAIL).by(1L))) - .containsExactly(3L); + .containsExactly(3L); assertThat(clusterConnection.stringCommands() .bitField(JedisConverters.toBytes(KEY_1), create().incr(unsigned(2)).valueAt(BitFieldSubCommands.Offset.offset(102L)).overflow(FAIL).by(1L)) @@ -2964,7 +3012,7 @@ void bitfieldShouldAllowMultipleSubcommands() { assertThat(clusterConnection.stringCommands().bitField(JedisConverters.toBytes(KEY_1), create().incr(signed(5)).valueAt(BitFieldSubCommands.Offset.offset(100L)).by(1L).get(unsigned(4)).valueAt(0L))) - .containsExactly(1L, 0L); + .containsExactly(1L, 0L); } @Test // DATAREDIS-562 @@ -2974,13 +3022,13 @@ void bitfieldShouldWorkUsingNonZeroBasedOffset() { clusterConnection.stringCommands().bitField(JedisConverters.toBytes(KEY_1), create().set(INT_8).valueAt(BitFieldSubCommands.Offset.offset(0L).multipliedByTypeLength()).to(100L) .set(INT_8).valueAt(BitFieldSubCommands.Offset.offset(1L).multipliedByTypeLength()).to(200L))) - .containsExactly(0L, 0L); + .containsExactly(0L, 0L); assertThat( clusterConnection.stringCommands() .bitField(JedisConverters.toBytes(KEY_1), create().get(INT_8).valueAt(BitFieldSubCommands.Offset.offset(0L).multipliedByTypeLength()).get(INT_8) - .valueAt(BitFieldSubCommands.Offset.offset(1L).multipliedByTypeLength()))).containsExactly(100L, - -56L); + .valueAt(BitFieldSubCommands.Offset.offset(1L).multipliedByTypeLength()))) + .containsExactly(100L, -56L); } @Test // DATAREDIS-1005 @@ -3125,7 +3173,8 @@ void shouldUseCachedTopology() { assertThat(topology).isInstanceOf(JedisClusterConnection.JedisClusterTopology.class); assertThat(provider.shouldUseCachedValue(null)).isFalse(); - assertThat(provider.shouldUseCachedValue(new JedisClusterConnection.JedisClusterTopology(Set.of(), System.currentTimeMillis() - 101, 100))).isFalse(); + assertThat(provider.shouldUseCachedValue( + new JedisClusterConnection.JedisClusterTopology(Set.of(), System.currentTimeMillis() - 101, 100))).isFalse(); assertThat(provider.shouldUseCachedValue( new JedisClusterConnection.JedisClusterTopology(Set.of(), System.currentTimeMillis() + 100, 100))).isTrue(); } diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java index 5611fb351f..1d45dc739e 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java @@ -15,16 +15,35 @@ */ package org.springframework.data.redis.connection.lettuce; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.data.Offset.offset; -import static org.springframework.data.redis.connection.BitFieldSubCommands.*; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.*; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.*; -import static org.springframework.data.redis.connection.ClusterTestVariables.*; -import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.*; -import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.*; -import static org.springframework.data.redis.connection.RedisZSetCommands.*; -import static org.springframework.data.redis.core.ScanOptions.*; +import static org.springframework.data.redis.connection.BitFieldSubCommands.create; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.FAIL; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.INT_8; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.signed; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.unsigned; +import static org.springframework.data.redis.connection.ClusterTestVariables.CLUSTER_HOST; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_1; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_2; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_3; +import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_4; +import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_1_PORT; +import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_2_PORT; +import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_3_PORT; +import static org.springframework.data.redis.connection.ClusterTestVariables.REPLICAOF_NODE_1_PORT; +import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_1; +import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_2; +import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_3; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_1; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_2; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_3; +import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_4; +import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.KILOMETERS; +import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs; +import static org.springframework.data.redis.connection.RedisZSetCommands.Range; +import static org.springframework.data.redis.core.ScanOptions.scanOptions; import io.lettuce.core.cluster.RedisClusterClient; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; @@ -33,7 +52,17 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.assertj.core.data.Offset; @@ -49,11 +78,21 @@ import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.Point; -import org.springframework.data.redis.connection.*; +import org.springframework.data.redis.connection.BitFieldSubCommands; +import org.springframework.data.redis.connection.ClusterConnectionTests; +import org.springframework.data.redis.connection.ClusterSlotHashUtil; +import org.springframework.data.redis.connection.ClusterTestVariables; +import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.DefaultSortParameters; import org.springframework.data.redis.connection.Limit; +import org.springframework.data.redis.connection.RedisClusterConfiguration; +import org.springframework.data.redis.connection.RedisClusterConnection; +import org.springframework.data.redis.connection.RedisClusterNode; import org.springframework.data.redis.connection.RedisClusterNode.SlotRange; import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation; +import org.springframework.data.redis.connection.RedisListCommands; import org.springframework.data.redis.connection.RedisListCommands.Position; +import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisServerCommands.FlushOption; import org.springframework.data.redis.connection.RedisStringCommands.BitOperation; import org.springframework.data.redis.connection.RedisStringCommands.SetOption; @@ -1097,7 +1136,7 @@ public void hStrLenReturnsZeroWhenKeyDoesNotExist() { assertThat(clusterConnection.hashCommands().hStrLen(KEY_1_BYTES, KEY_1_BYTES)).isEqualTo(0L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireReturnsSuccessAndSetsTTL() { @@ -1107,7 +1146,7 @@ public void hExpireReturnsSuccessAndSetsTTL() { assertThat(clusterConnection.hTtl(KEY_1_BYTES, KEY_2_BYTES)).allSatisfy(val -> assertThat(val).isBetween(0L, 5L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { @@ -1118,17 +1157,19 @@ public void hExpireReturnsMinusTwoWhenFieldDoesNotExist() { assertThat(clusterConnection.hashCommands().hExpire(KEY_2_BYTES, 5L, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsSuccessAndSetsTTL() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 5000L, KEY_2_BYTES)).contains(1L); @@ -1136,7 +1177,7 @@ public void hpExpireReturnsSuccessAndSetsTTL() { .allSatisfy(val -> assertThat(val).isBetween(0L, 5000L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { @@ -1147,7 +1188,7 @@ public void hpExpireReturnsMinusTwoWhenFieldDoesNotExist() { assertThat(clusterConnection.hashCommands().hpExpire(KEY_2_BYTES, 5L, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireReturnsTwoWhenZeroProvided() { @@ -1156,17 +1197,18 @@ public void hpExpireReturnsTwoWhenZeroProvided() { assertThat(clusterConnection.hashCommands().hpExpire(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireAtReturnsSuccessAndSetsTTL() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).getEpochSecond(); + assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); assertThat(clusterConnection.hTtl(KEY_1_BYTES, KEY_2_BYTES)).allSatisfy(val -> assertThat(val).isBetween(0L, 5L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { @@ -1180,9 +1222,10 @@ public void hExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { assertThat(clusterConnection.hashCommands().hExpireAt(KEY_2_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hExpireAdReturnsTwoWhenZeroProvided() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hExpireAt(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); @@ -1194,12 +1237,13 @@ public void hpExpireAtReturnsSuccessAndSetsTTL() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); long inFiveSeconds = Instant.now().plusSeconds(5L).toEpochMilli(); + assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(1L); assertThat(clusterConnection.hpTtl(KEY_1_BYTES, KEY_2_BYTES)) .allSatisfy(val -> assertThat(val).isGreaterThan(1000L).isLessThanOrEqualTo(5000L)); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { @@ -1212,7 +1256,7 @@ public void hpExpireAtReturnsMinusTwoWhenFieldDoesNotExist() { assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_2_BYTES, inFiveSeconds, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hpExpireAdReturnsTwoWhenZeroProvided() { @@ -1221,17 +1265,17 @@ public void hpExpireAdReturnsTwoWhenZeroProvided() { assertThat(clusterConnection.hashCommands().hpExpireAt(KEY_1_BYTES, 0L, KEY_2_BYTES)).contains(2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hPersistReturnsSuccessAndPersistsField() { nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hExpire(KEY_1_BYTES, 5L, KEY_2_BYTES)).contains(1L); - assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(1L); + assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(1L); assertThat(clusterConnection.hTtl(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { @@ -1239,17 +1283,18 @@ public void hPersistReturnsMinusOneWhenFieldDoesNotHaveExpiration() { assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_2_BYTES)).contains(-1L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hPersistReturnsMinusTwoWhenFieldOrKeyMissing() { + nativeConnection.hset(KEY_1, KEY_2, VALUE_3); assertThat(clusterConnection.hashCommands().hPersist(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); - assertThat(clusterConnection.hashCommands().hPersist(KEY_3_BYTES,KEY_2_BYTES)).contains(-2L); + assertThat(clusterConnection.hashCommands().hPersist(KEY_3_BYTES, KEY_2_BYTES)).contains(-2L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { @@ -1259,12 +1304,12 @@ public void hTtlReturnsMinusOneWhenFieldHasNoExpiration() { assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, TimeUnit.HOURS, KEY_2_BYTES)).contains(-1L); } - @Test + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") public void hTtlReturnsMinusTwoWhenFieldOrKeyMissing() { assertThat(clusterConnection.hashCommands().hTtl(KEY_1_BYTES, KEY_1_BYTES)).contains(-2L); - assertThat(clusterConnection.hashCommands().hTtl(KEY_3_BYTES,KEY_2_BYTES)).contains(-2L); + assertThat(clusterConnection.hashCommands().hTtl(KEY_3_BYTES, KEY_2_BYTES)).contains(-2L); } @Test // DATAREDIS-315 diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java index 4ef5fcffe3..bc1f8cc204 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommandsIntegrationTests.java @@ -15,9 +15,8 @@ */ package org.springframework.data.redis.connection.lettuce; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; -import org.springframework.data.redis.test.condition.EnabledOnCommand; import reactor.test.StepVerifier; import java.nio.ByteBuffer; @@ -31,6 +30,7 @@ import java.util.Map; import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest; /** @@ -107,8 +107,7 @@ void hMGetShouldReturnValueForFields() { nativeCommands.hset(KEY_1, FIELD_3, VALUE_3); connection.hashCommands().hMGet(KEY_1_BBUFFER, Arrays.asList(FIELD_1_BBUFFER, FIELD_3_BBUFFER)) - .as(StepVerifier::create) - .consumeNextWith(actual -> { + .as(StepVerifier::create).consumeNextWith(actual -> { assertThat(actual).contains(VALUE_1_BBUFFER, VALUE_3_BBUFFER); @@ -124,13 +123,11 @@ void hMGetShouldReturnNullValueForFieldsThatHaveNoValue() { connection.hashCommands().hMGet(KEY_1_BBUFFER, Collections.singletonList(FIELD_1_BBUFFER)).as(StepVerifier::create) .expectNext(Collections.singletonList(VALUE_1_BBUFFER)).verifyComplete(); - connection.hashCommands().hMGet(KEY_1_BBUFFER, Collections.singletonList(FIELD_2_BBUFFER)) - .as(StepVerifier::create) + connection.hashCommands().hMGet(KEY_1_BBUFFER, Collections.singletonList(FIELD_2_BBUFFER)).as(StepVerifier::create) .expectNext(Collections.singletonList(null)).verifyComplete(); connection.hashCommands().hMGet(KEY_1_BBUFFER, Arrays.asList(FIELD_1_BBUFFER, FIELD_2_BBUFFER, FIELD_3_BBUFFER)) - .as(StepVerifier::create) - .expectNext(Arrays.asList(VALUE_1_BBUFFER, null, VALUE_3_BBUFFER)).verifyComplete(); + .as(StepVerifier::create).expectNext(Arrays.asList(VALUE_1_BBUFFER, null, VALUE_3_BBUFFER)).verifyComplete(); } @ParameterizedRedisTest // DATAREDIS-525 @@ -197,8 +194,7 @@ void hDelShouldRemoveMultipleFieldsCorrectly() { nativeCommands.hset(KEY_1, FIELD_3, VALUE_3); connection.hashCommands().hDel(KEY_1_BBUFFER, Arrays.asList(FIELD_1_BBUFFER, FIELD_3_BBUFFER)) - .as(StepVerifier::create) - .expectNext(2L).verifyComplete(); + .as(StepVerifier::create).expectNext(2L).verifyComplete(); } @ParameterizedRedisTest // DATAREDIS-525 @@ -293,62 +289,50 @@ void hStrLenReturnsZeroWhenKeyDoesNotExist() { .verifyComplete(); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void hExpireShouldHandleMultipleParametersCorrectly() { + assertThat(nativeCommands.hset(KEY_1, FIELD_1, VALUE_1)).isTrue(); assertThat(nativeCommands.hset(KEY_1, FIELD_2, VALUE_2)).isTrue(); final var fields = Arrays.asList(FIELD_1_BBUFFER, FIELD_2_BBUFFER, FIELD_3_BBUFFER); connection.hashCommands().hExpire(KEY_1_BBUFFER, Duration.ofSeconds(1), fields).as(StepVerifier::create) // - .expectNext(1L) - .expectNext(1L) - .expectNext(-2L) - .expectComplete() - .verify(); + .expectNext(1L).expectNext(1L).expectNext(-2L).expectComplete().verify(); assertThat(nativeCommands.httl(KEY_1, FIELD_1)).allSatisfy(it -> assertThat(it).isBetween(0L, 1000L)); assertThat(nativeCommands.httl(KEY_1, FIELD_2)).allSatisfy(it -> assertThat(it).isBetween(0L, 1000L)); assertThat(nativeCommands.httl(KEY_1, FIELD_3)).allSatisfy(it -> assertThat(it).isEqualTo(-2L)); - } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void hExpireAtShouldHandleMultipleParametersCorrectly() { + assertThat(nativeCommands.hset(KEY_1, FIELD_1, VALUE_1)).isTrue(); assertThat(nativeCommands.hset(KEY_1, FIELD_2, VALUE_2)).isTrue(); final var fields = Arrays.asList(FIELD_1_BBUFFER, FIELD_2_BBUFFER, FIELD_3_BBUFFER); connection.hashCommands().hExpireAt(KEY_1_BBUFFER, Instant.now().plusSeconds(1), fields).as(StepVerifier::create) // - .expectNext(1L) - .expectNext(1L) - .expectNext(-2L) - .expectComplete() - .verify(); + .expectNext(1L).expectNext(1L).expectNext(-2L).expectComplete().verify(); assertThat(nativeCommands.httl(KEY_1, FIELD_1, FIELD_2)).allSatisfy(it -> assertThat(it).isBetween(0L, 1000L)); assertThat(nativeCommands.httl(KEY_1, FIELD_3)).allSatisfy(it -> assertThat(it).isEqualTo(-2L)); - } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void hPersistShouldPersistFields() { + assertThat(nativeCommands.hset(KEY_1, FIELD_1, VALUE_1)).isTrue(); assertThat(nativeCommands.hset(KEY_1, FIELD_2, VALUE_2)).isTrue(); - assertThat(nativeCommands.hexpire(KEY_1, 1000, FIELD_1)) - .allSatisfy(it -> assertThat(it).isEqualTo(1L)); + assertThat(nativeCommands.hexpire(KEY_1, 1000, FIELD_1)).allSatisfy(it -> assertThat(it).isEqualTo(1L)); final var fields = Arrays.asList(FIELD_1_BBUFFER, FIELD_2_BBUFFER, FIELD_3_BBUFFER); connection.hashCommands().hPersist(KEY_1_BBUFFER, fields).as(StepVerifier::create) // - .expectNext(1L) - .expectNext(-1L) - .expectNext(-2L) - .expectComplete() - .verify(); + .expectNext(1L).expectNext(-1L).expectNext(-2L).expectComplete().verify(); assertThat(nativeCommands.httl(KEY_1, FIELD_1, FIELD_2)).allSatisfy(it -> assertThat(it).isEqualTo(-1L)); } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java index bbb84a76de..6499ae325a 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java @@ -15,8 +15,9 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assumptions.assumeThat; import java.io.IOException; import java.time.Duration; @@ -29,7 +30,6 @@ import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; - import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.StringObjectFactory; @@ -212,7 +212,7 @@ void randomValue() { assertThat(values).hasSize(2).containsEntry(key1, val1).containsEntry(key2, val2); } - @EnabledOnCommand("HEXPIRE") + @EnabledOnCommand("HEXPIRE") // GH-3054 @ParameterizedRedisTest void testExpireAndGetExpireMillis() { @@ -237,7 +237,7 @@ void testExpireAndGetExpireMillis() { }); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testExpireAndGetExpireSeconds() { @@ -268,7 +268,7 @@ void testExpireAndGetExpireSeconds() { }); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testBoundExpireAndGetExpireSeconds() { @@ -300,7 +300,7 @@ void testBoundExpireAndGetExpireSeconds() { }); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testExpireAtAndGetExpireMillis() { @@ -325,7 +325,7 @@ void testExpireAtAndGetExpireMillis() { }); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void expireThrowsErrorOfNanoPrecision() { @@ -336,7 +336,7 @@ void expireThrowsErrorOfNanoPrecision() { .isThrownBy(() -> redisTemplate.opsForHash().getTimeToLive(key, TimeUnit.NANOSECONDS, List.of(key1))); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testExpireWithOptionsNone() { @@ -349,12 +349,13 @@ void testExpireWithOptionsNone() { hashOps.put(key, key1, val1); hashOps.put(key, key2, val2); - ExpireChanges expire = redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), List.of(key1)); + ExpireChanges expire = redisTemplate.opsForHash().expire(key, + org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), List.of(key1)); assertThat(expire.allOk()).isTrue(); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testExpireWithOptions() { @@ -367,16 +368,20 @@ void testExpireWithOptions() { hashOps.put(key, key1, val1); hashOps.put(key, key2, val2); - redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), List.of(key1)); - redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(60), FieldExpirationOptions.none(), List.of(key2)); + redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), + FieldExpirationOptions.none(), List.of(key1)); + redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(60), + FieldExpirationOptions.none(), List.of(key2)); - ExpireChanges changes = redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(30), FieldExpirationOptions.builder().gt().build(), List.of(key1, key2)); + ExpireChanges changes = redisTemplate.opsForHash().expire(key, + org.springframework.data.redis.core.types.Expiration.seconds(30), FieldExpirationOptions.builder().gt().build(), + List.of(key1, key2)); assertThat(changes.ok()).containsExactly(key1); assertThat(changes.skipped()).containsExactly(key2); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testPersistAndGetExpireMillis() { diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java index 2d574ee123..48532b2feb 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java @@ -15,9 +15,9 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; -import static org.junit.jupiter.api.condition.OS.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.junit.jupiter.api.condition.OS.MAC; import reactor.test.StepVerifier; @@ -33,7 +33,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.condition.DisabledOnOs; - import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.SettingsUtils; @@ -506,7 +505,7 @@ void scan() { .verifyComplete(); } - @EnabledOnCommand("HEXPIRE") + @EnabledOnCommand("HEXPIRE") // GH-3054 @ParameterizedRedisTest void testExpireAndGetExpireMillis() { @@ -531,7 +530,7 @@ void testExpireAndGetExpireMillis() { }).verifyComplete(); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testExpireWithOptions() { @@ -543,23 +542,32 @@ void testExpireWithOptions() { putAll(key, key1, val1, key2, val2); - hashOperations.expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), List.of(key1)).as(StepVerifier::create)// - .assertNext(changes -> { - assertThat(changes.allOk()).isTrue(); - }).verifyComplete(); - hashOperations.expire(key, org.springframework.data.redis.core.types.Expiration.seconds(60), FieldExpirationOptions.none(), List.of(key2)).as(StepVerifier::create)// - .assertNext(changes -> { - assertThat(changes.allOk()).isTrue(); - }).verifyComplete(); - - hashOperations.expire(key, org.springframework.data.redis.core.types.Expiration.seconds(30), FieldExpirationOptions.builder().gt().build(), List.of(key1, key2)).as(StepVerifier::create)// - .assertNext(changes -> { - assertThat(changes.ok()).containsExactly(key1); - assertThat(changes.skipped()).containsExactly(key2); - }).verifyComplete(); + hashOperations + .expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), + List.of(key1)) + .as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.allOk()).isTrue(); + }).verifyComplete(); + hashOperations + .expire(key, org.springframework.data.redis.core.types.Expiration.seconds(60), FieldExpirationOptions.none(), + List.of(key2)) + .as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.allOk()).isTrue(); + }).verifyComplete(); + + hashOperations + .expire(key, org.springframework.data.redis.core.types.Expiration.seconds(30), + FieldExpirationOptions.builder().gt().build(), List.of(key1, key2)) + .as(StepVerifier::create)// + .assertNext(changes -> { + assertThat(changes.ok()).containsExactly(key1); + assertThat(changes.skipped()).containsExactly(key2); + }).verifyComplete(); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testExpireAndGetExpireSeconds() { @@ -583,10 +591,9 @@ void testExpireAndGetExpireSeconds() { assertThat(it.expirationOf(key1).raw()).isBetween(0L, 5L); assertThat(it.expirationOf(key2).raw()).isBetween(0L, 5L); }).verifyComplete(); - } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testExpireAtAndGetExpireMillis() { @@ -611,7 +618,7 @@ void testExpireAtAndGetExpireMillis() { }).verifyComplete(); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testPersistAndGetExpireMillis() { @@ -638,7 +645,6 @@ void testPersistAndGetExpireMillis() { .assertNext(expirations -> { assertThat(expirations.persistent()).contains(key1, key2); }).verifyComplete(); - } @ParameterizedRedisTest // DATAREDIS-602 diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java index 0a03b7340e..31e93d06ac 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java @@ -15,8 +15,9 @@ */ package org.springframework.data.redis.support.collections; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.*; -import static org.assertj.core.api.Assumptions.*; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assumptions.assumeThat; import java.io.IOException; import java.text.DecimalFormat; @@ -35,7 +36,6 @@ import org.assertj.core.api.Assumptions; import org.junit.jupiter.api.BeforeEach; - import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.DoubleAsStringObjectFactory; import org.springframework.data.redis.LongAsStringObjectFactory; @@ -197,7 +197,7 @@ void testIncrement() { assertThat(map.increment(k1, 10)).isEqualTo(Long.valueOf(Long.valueOf((String) v1) + 10)); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testExpire() { @@ -216,7 +216,7 @@ void testExpire() { assertThat(ops.persist()).satisfies(ExpireChanges::allOk); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-3054 @EnabledOnCommand("HEXPIRE") void testExpireAt() { From 08d66aa10da58b9589c318199d7ea98acb64bdc8 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 11 Mar 2025 11:03:10 +0100 Subject: [PATCH 141/187] Remove links to gitter. Closes: #3117 --- README.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index d6e327b798..93d3896580 100644 --- a/README.adoc +++ b/README.adoc @@ -1,4 +1,4 @@ -= Spring Data Redis image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-redis%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-redis/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=Spring%20Data%20Redis"] += Spring Data Redis image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-redis%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-redis/] image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=Spring%20Data%20Redis"] The primary goal of the https://spring.io/projects/spring-data/[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services. @@ -100,7 +100,6 @@ https://docs.spring.io/spring-data/redis/reference/[reference documentation], an If you are just starting out with Spring, try one of the https://spring.io/guides[guides]. * If you are upgrading, check out the https://github.com/spring-projects/spring-data-commons/wiki#release-notes[Release notes] for "`new and noteworthy`" features. * Ask a question - we monitor https://stackoverflow.com[stackoverflow.com] for questions tagged with https://stackoverflow.com/tags/spring-data[`spring-data-redis`]. -You can also chat with the community on https://gitter.im/spring-projects/spring-data[Gitter]. * Report bugs with Spring Data Redis at https://github.com/spring-projects/spring-data-redis/issues/new[github.com/spring-projects/spring-data-redis]. == Reporting Issues From b8d289208e3a66aeb45d738c5f02dcbae8b1023f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 5 Mar 2025 16:40:00 +0100 Subject: [PATCH 142/187] Add support for conditional key expiry (XX/NX/LT/GT). Move ExpirationOptions from Hash to top-level. Closes: #3114 Original Pull Request: #3115 --- .../DefaultStringRedisConnection.java | 59 ++++--- .../connection/DefaultedRedisConnection.java | 59 +++++-- .../redis/connection/ExpirationOptions.java | 145 ++++++++++++++++ .../data/redis/connection/Hash.java | 137 ---------------- .../connection/ReactiveHashCommands.java | 62 +++---- .../redis/connection/ReactiveKeyCommands.java | 120 +++++++++++++- .../redis/connection/RedisHashCommands.java | 29 ++-- .../redis/connection/RedisKeyCommands.java | 155 ++++++++++++++++-- .../connection/StringRedisConnection.java | 84 ++++++++-- .../jedis/JedisClusterHashCommands.java | 18 +- .../jedis/JedisClusterKeyCommands.java | 37 ++++- .../connection/jedis/JedisHashCommands.java | 18 +- .../connection/jedis/JedisKeyCommands.java | 46 +++++- .../lettuce/LettuceHashCommands.java | 14 +- .../lettuce/LettuceKeyCommands.java | 35 +++- .../lettuce/LettuceReactiveHashCommands.java | 7 +- .../lettuce/LettuceReactiveKeyCommands.java | 69 ++++++-- .../BoundHashFieldExpirationOperations.java | 9 +- .../data/redis/core/BoundHashOperations.java | 8 +- ...ultBoundHashFieldExpirationOperations.java | 4 +- .../redis/core/DefaultHashOperations.java | 6 +- .../core/DefaultReactiveHashOperations.java | 11 +- .../data/redis/core/ExpireChanges.java | 4 + .../data/redis/core/HashOperations.java | 12 +- .../redis/core/ReactiveHashOperations.java | 4 +- .../redis/core/ReactiveRedisOperations.java | 19 +++ .../redis/core/ReactiveRedisTemplate.java | 16 ++ .../data/redis/core/RedisCommand.java | 8 +- .../data/redis/core/RedisOperations.java | 20 +++ .../data/redis/core/RedisTemplate.java | 12 ++ .../support/collections/DefaultRedisMap.java | 8 +- .../redis/support/collections/RedisMap.java | 17 +- .../support/collections/RedisProperties.java | 8 +- .../AbstractConnectionIntegrationTests.java | 88 +++++----- .../connection/ClusterConnectionTests.java | 4 +- .../DefaultStringRedisConnectionTests.java | 16 +- .../jedis/JedisClusterConnectionTests.java | 129 +++++++++------ .../LettuceClusterConnectionTests.java | 128 +++++++++------ ...ceReactiveKeyCommandsIntegrationTests.java | 33 ++++ ...DefaultHashOperationsIntegrationTests.java | 18 +- ...eactiveHashOperationsIntegrationTests.java | 15 +- ...ReactiveRedisTemplateIntegrationTests.java | 38 +++++ .../AbstractRedisMapIntegrationTests.java | 10 +- 43 files changed, 1192 insertions(+), 547 deletions(-) create mode 100644 src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java delete mode 100644 src/main/java/org/springframework/data/redis/connection/Hash.java diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java index 831a46ece2..c2ea198d8b 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -31,7 +31,6 @@ import org.springframework.data.geo.Metric; import org.springframework.data.geo.Point; import org.springframework.data.redis.RedisSystemException; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.connection.convert.ListConverter; import org.springframework.data.redis.connection.convert.MapConverter; @@ -359,13 +358,13 @@ public Long exists(byte[]... keys) { } @Override - public Boolean expire(byte[] key, long seconds) { - return convertAndReturn(delegate.expire(key, seconds), Converters.identityConverter()); + public Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) { + return convertAndReturn(delegate.expire(key, seconds, condition), Converters.identityConverter()); } @Override - public Boolean expireAt(byte[] key, long unixTime) { - return convertAndReturn(delegate.expireAt(key, unixTime), Converters.identityConverter()); + public Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) { + return convertAndReturn(delegate.expireAt(key, unixTime, condition), Converters.identityConverter()); } @Override @@ -1308,13 +1307,13 @@ public Long zLexCount(String key, org.springframework.data.domain.Range } @Override - public Boolean pExpire(byte[] key, long millis) { - return convertAndReturn(delegate.pExpire(key, millis), Converters.identityConverter()); + public Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) { + return convertAndReturn(delegate.pExpire(key, millis, condition), Converters.identityConverter()); } @Override - public Boolean pExpireAt(byte[] key, long unixTimeInMillis) { - return convertAndReturn(delegate.pExpireAt(key, unixTimeInMillis), Converters.identityConverter()); + public Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) { + return convertAndReturn(delegate.pExpireAt(key, unixTimeInMillis, condition), Converters.identityConverter()); } @Override @@ -1497,13 +1496,13 @@ public Boolean exists(String key) { } @Override - public Boolean expire(String key, long seconds) { - return expire(serialize(key), seconds); + public Boolean expire(String key, long seconds, ExpirationOptions.Condition condition) { + return expire(serialize(key), seconds, condition); } @Override - public Boolean expireAt(String key, long unixTime) { - return expireAt(serialize(key), unixTime); + public Boolean expireAt(String key, long unixTime, ExpirationOptions.Condition condition) { + return expireAt(serialize(key), unixTime, condition); } @Override @@ -2491,13 +2490,13 @@ public Object execute(String command, String... args) { } @Override - public Boolean pExpire(String key, long millis) { - return pExpire(serialize(key), millis); + public Boolean pExpire(String key, long millis, ExpirationOptions.Condition condition) { + return pExpire(serialize(key), millis, condition); } @Override - public Boolean pExpireAt(String key, long unixTimeInMillis) { - return pExpireAt(serialize(key), unixTimeInMillis); + public Boolean pExpireAt(String key, long unixTimeInMillis, ExpirationOptions.Condition condition) { + return pExpireAt(serialize(key), unixTimeInMillis, condition); } @Override @@ -2581,29 +2580,29 @@ public Long hStrLen(byte[] key, byte[] field) { return convertAndReturn(delegate.hStrLen(key, field), Converters.identityConverter()); } - public @Nullable List applyExpiration(byte[] key, + public @Nullable List applyHashFieldExpiration(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, - FieldExpirationOptions options, byte[]... fields) { - return this.delegate.applyExpiration(key, expiration, options, fields); + ExpirationOptions options, byte[]... fields) { + return this.delegate.applyHashFieldExpiration(key, expiration, options, fields); } @Override - public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) { return this.delegate.hExpire(key, seconds, condition, fields); } @Override - public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) { return this.delegate.hpExpire(key, millis, condition, fields); } @Override - public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) { return this.delegate.hExpireAt(key, unixTime, condition, fields); } @Override - public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + public List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition, byte[]... fields) { return this.delegate.hpExpireAt(key, unixTimeInMillis, condition, fields); } @@ -2630,27 +2629,27 @@ public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) { public @Nullable List applyExpiration(String key, org.springframework.data.redis.core.types.Expiration expiration, - FieldExpirationOptions options, String... fields) { - return applyExpiration(serialize(key), expiration, options, serializeMulti(fields)); + ExpirationOptions options, String... fields) { + return this.applyHashFieldExpiration(serialize(key), expiration, options, serializeMulti(fields)); } @Override - public List hExpire(String key, long seconds, FieldExpirationOptions.Condition condition, String... fields) { + public List hExpire(String key, long seconds, ExpirationOptions.Condition condition, String... fields) { return hExpire(serialize(key), seconds, condition, serializeMulti(fields)); } @Override - public List hpExpire(String key, long millis, FieldExpirationOptions.Condition condition, String... fields) { + public List hpExpire(String key, long millis, ExpirationOptions.Condition condition, String... fields) { return hpExpire(serialize(key), millis, condition, serializeMulti(fields)); } @Override - public List hExpireAt(String key, long unixTime, FieldExpirationOptions.Condition condition, String... fields) { + public List hExpireAt(String key, long unixTime, ExpirationOptions.Condition condition, String... fields) { return hExpireAt(serialize(key), unixTime, condition, serializeMulti(fields)); } @Override - public List hpExpireAt(String key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + public List hpExpireAt(String key, long unixTimeInMillis, ExpirationOptions.Condition condition, String... fields) { return hpExpireAt(serialize(key), unixTimeInMillis, condition, serializeMulti(fields)); } diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java index 8190caa6a0..f3be7ab276 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -28,7 +28,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.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.stream.ByteRecord; import org.springframework.data.redis.connection.stream.Consumer; import org.springframework.data.redis.connection.stream.MapRecord; @@ -162,7 +161,14 @@ default Boolean renameNX(byte[] sourceKey, byte[] targetKey) { @Override @Deprecated default Boolean expire(byte[] key, long seconds) { - return keyCommands().expire(key, seconds); + return keyCommands().expire(key, seconds, ExpirationOptions.Condition.ALWAYS); + } + + /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */ + @Override + @Deprecated + default Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) { + return keyCommands().expire(key, seconds, condition); } /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */ @@ -204,21 +210,42 @@ default Long pTtl(byte[] key, TimeUnit timeUnit) { @Override @Deprecated default Boolean pExpire(byte[] key, long millis) { - return keyCommands().pExpire(key, millis); + return keyCommands().pExpire(key, millis, ExpirationOptions.Condition.ALWAYS); + } + + /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */ + @Override + @Deprecated + default Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) { + return keyCommands().pExpire(key, millis, condition); } /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */ @Override @Deprecated default Boolean pExpireAt(byte[] key, long unixTimeInMillis) { - return keyCommands().pExpireAt(key, unixTimeInMillis); + return keyCommands().pExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS); + } + + /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */ + @Override + @Deprecated + default Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) { + return keyCommands().pExpireAt(key, unixTimeInMillis, condition); } /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */ @Override @Deprecated default Boolean expireAt(byte[] key, long unixTime) { - return keyCommands().expireAt(key, unixTime); + return keyCommands().expireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS); + } + + /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */ + @Override + @Deprecated + default Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) { + return keyCommands().expireAt(key, unixTime, condition); } /** @deprecated in favor of {@link RedisConnection#keyCommands()}. */ @@ -1483,13 +1510,13 @@ default Long hStrLen(byte[] key, byte[] field) { @Override @Deprecated default List hExpire(byte[] key, long seconds, byte[]... fields) { - return hashCommands().hExpire(key, seconds, FieldExpirationOptions.Condition.ALWAYS, fields); + return hashCommands().hExpire(key, seconds, ExpirationOptions.Condition.ALWAYS, fields); } /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @Override @Deprecated - default List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { + default List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) { return hashCommands().hExpire(key, seconds, condition, fields); } @@ -1497,13 +1524,13 @@ default List hExpire(byte[] key, long seconds, FieldExpirationOptions.Cond @Override @Deprecated default List hpExpire(byte[] key, long millis, byte[]... fields) { - return hashCommands().hpExpire(key, millis, FieldExpirationOptions.Condition.ALWAYS, fields); + return hashCommands().hpExpire(key, millis, ExpirationOptions.Condition.ALWAYS, fields); } /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @Override @Deprecated - default List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { + default List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) { return hashCommands().hpExpire(key, millis, condition, fields); } @@ -1511,13 +1538,13 @@ default List hpExpire(byte[] key, long millis, FieldExpirationOptions.Cond @Override @Deprecated default List hExpireAt(byte[] key, long unixTime, byte[]... fields) { - return hashCommands().hExpireAt(key, unixTime, FieldExpirationOptions.Condition.ALWAYS, fields); + return hashCommands().hExpireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS, fields); } /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @Override @Deprecated - default List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, + default List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) { return hashCommands().hExpireAt(key, unixTime, condition, fields); } @@ -1526,13 +1553,13 @@ default List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.C @Override @Deprecated default List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { - return hashCommands().hpExpireAt(key, unixTimeInMillis, FieldExpirationOptions.Condition.ALWAYS, fields); + return hashCommands().hpExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS, fields); } /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @Override @Deprecated - default List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + default List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition, byte[]... fields) { return hashCommands().hpExpireAt(key, unixTimeInMillis, condition, fields); } @@ -1568,10 +1595,10 @@ default List hpTtl(byte[] key, byte[]... fields) { /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ @Override @Deprecated - default @Nullable List applyExpiration(byte[] key, - org.springframework.data.redis.core.types.Expiration expiration, FieldExpirationOptions options, + default @Nullable List applyHashFieldExpiration(byte[] key, + org.springframework.data.redis.core.types.Expiration expiration, ExpirationOptions options, byte[]... fields) { - return hashCommands().applyExpiration(key, expiration, options, fields); + return hashCommands().applyHashFieldExpiration(key, expiration, options, fields); } // GEO COMMANDS diff --git a/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java b/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java new file mode 100644 index 0000000000..2780b6149c --- /dev/null +++ b/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java @@ -0,0 +1,145 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.connection; + +import java.util.Objects; + +import org.springframework.lang.Contract; +import org.springframework.util.ObjectUtils; + +/** + * Expiration options for Expiation updates. + * + * @author Christoph Strobl + * @author Mark Paluch + * @since 3.5 + */ +public class ExpirationOptions { + + private static final ExpirationOptions NONE = new ExpirationOptions(Condition.ALWAYS); + private final Condition condition; + + ExpirationOptions(Condition condition) { + this.condition = condition; + } + + /** + * @return an empty expiration options object. + */ + public static ExpirationOptions none() { + return NONE; + } + + /** + * @return builder for creating {@code FieldExpireOptionsBuilder}. + */ + public static FieldExpireOptionsBuilder builder() { + return new FieldExpireOptionsBuilder(); + } + + public Condition getCondition() { + return condition; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ExpirationOptions that = (ExpirationOptions) o; + return ObjectUtils.nullSafeEquals(this.condition, that.condition); + } + + @Override + public int hashCode() { + return Objects.hash(condition); + } + + /** + * Builder to build {@link ExpirationOptions} + */ + public static class FieldExpireOptionsBuilder { + + private Condition condition = Condition.ALWAYS; + + private FieldExpireOptionsBuilder() {} + + @Contract("-> this") + public FieldExpireOptionsBuilder nx() { + this.condition = Condition.NX; + return this; + } + + @Contract("-> this") + public FieldExpireOptionsBuilder xx() { + this.condition = Condition.XX; + return this; + } + + @Contract("-> this") + public FieldExpireOptionsBuilder gt() { + this.condition = Condition.GT; + return this; + } + + @Contract("-> this") + public FieldExpireOptionsBuilder lt() { + this.condition = Condition.LT; + return this; + } + + public ExpirationOptions build() { + return condition == Condition.ALWAYS ? NONE : new ExpirationOptions(condition); + } + + } + + /** + * Conditions to apply when changing expiration. + */ + public enum Condition { + + /** + * Always apply expiration. + */ + ALWAYS, + + /** + * Set expiration only when the field has no expiration. + */ + NX, + + /** + * Set expiration only when the field has an existing expiration. + */ + XX, + + /** + * Set expiration only when the new expiration is greater than current one. + */ + GT, + + /** + * Set expiration only when the new expiration is greater than current one. + */ + LT + + } + +} diff --git a/src/main/java/org/springframework/data/redis/connection/Hash.java b/src/main/java/org/springframework/data/redis/connection/Hash.java deleted file mode 100644 index 51e326dd2b..0000000000 --- a/src/main/java/org/springframework/data/redis/connection/Hash.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection; - -import java.util.Objects; - -import org.springframework.lang.Contract; -import org.springframework.util.ObjectUtils; - -/** - * Types for interacting with Hash data structures. - * - * @author Christoph Strobl - * @since 3.5 - */ -public interface Hash { - - /** - * Expiration options for Hash Expiation updates. - */ - class FieldExpirationOptions { - - private static final FieldExpirationOptions NONE = new FieldExpirationOptions(Condition.ALWAYS); - private final Condition condition; - - FieldExpirationOptions(Condition condition) { - this.condition = condition; - } - - public static FieldExpirationOptions none() { - return NONE; - } - - public static FieldExpireOptionsBuilder builder() { - return new FieldExpireOptionsBuilder(); - } - - public Condition getCondition() { - return condition; - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - FieldExpirationOptions that = (FieldExpirationOptions) o; - return ObjectUtils.nullSafeEquals(this.condition, that.condition); - } - - @Override - public int hashCode() { - return Objects.hash(condition); - } - - public static class FieldExpireOptionsBuilder { - - private Condition condition = Condition.ALWAYS; - - @Contract("-> this") - public FieldExpireOptionsBuilder nx() { - this.condition = Condition.NX; - return this; - } - - @Contract("-> this") - public FieldExpireOptionsBuilder xx() { - this.condition = Condition.XX; - return this; - } - - @Contract("-> this") - public FieldExpireOptionsBuilder gt() { - this.condition = Condition.GT; - return this; - } - - @Contract("-> this") - public FieldExpireOptionsBuilder lt() { - this.condition = Condition.LT; - return this; - } - - public FieldExpirationOptions build() { - return condition == Condition.ALWAYS ? NONE : new FieldExpirationOptions(condition); - } - - } - - public enum Condition { - - /** - * Always apply expiration. - */ - ALWAYS, - - /** - * Set expiration only when the field has no expiration. - */ - NX, - - /** - * Set expiration only when the field has an existing expiration. - */ - XX, - - /** - * Set expiration only when the new expiration is greater than current one. - */ - GT, - - /** - * Set expiration only when the new expiration is greater than current one. - */ - LT - - } - - } - -} diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java index c463737747..385b652f2b 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java @@ -31,7 +31,6 @@ import org.reactivestreams.Publisher; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.Command; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; @@ -851,13 +850,13 @@ default Mono hStrLen(ByteBuffer key, ByteBuffer field) { /** * @since 3.5 */ - class ExpireCommand extends HashFieldsCommand { + class HashExpireCommand extends HashFieldsCommand { private final Expiration expiration; - private final FieldExpirationOptions options; + private final ExpirationOptions options; - private ExpireCommand(@Nullable ByteBuffer key, List fields, Expiration expiration, - FieldExpirationOptions options) { + private HashExpireCommand(@Nullable ByteBuffer key, List fields, Expiration expiration, + ExpirationOptions options) { super(key, fields); @@ -866,52 +865,52 @@ private ExpireCommand(@Nullable ByteBuffer key, List fields, Expirat } /** - * Creates a new {@link ExpireCommand}. + * Creates a new {@link HashExpireCommand}. * * @param fields the {@code field} names to apply expiration to * @param timeout the actual timeout * @param unit the unit of measure for the {@code timeout}. - * @return new instance of {@link ExpireCommand}. + * @return new instance of {@link HashExpireCommand}. */ - public static ExpireCommand expire(List fields, long timeout, TimeUnit unit) { + public static HashExpireCommand expire(List fields, long timeout, TimeUnit unit) { Assert.notNull(fields, "Field must not be null"); return expire(fields, Expiration.from(timeout, unit)); } /** - * Creates a new {@link ExpireCommand}. + * Creates a new {@link HashExpireCommand}. * * @param fields the {@code field} names to apply expiration to. * @param ttl the actual timeout. - * @return new instance of {@link ExpireCommand}. + * @return new instance of {@link HashExpireCommand}. */ - public static ExpireCommand expire(List fields, Duration ttl) { + public static HashExpireCommand expire(List fields, Duration ttl) { Assert.notNull(fields, "Field must not be null"); return expire(fields, Expiration.from(ttl)); } /** - * Creates a new {@link ExpireCommand}. + * Creates a new {@link HashExpireCommand}. * * @param fields the {@code field} names to apply expiration to * @param expiration the {@link Expiration} to apply to the given {@literal fields}. - * @return new instance of {@link ExpireCommand}. + * @return new instance of {@link HashExpireCommand}. */ - public static ExpireCommand expire(List fields, Expiration expiration) { - return new ExpireCommand(null, fields, expiration, FieldExpirationOptions.none()); + public static HashExpireCommand expire(List fields, Expiration expiration) { + return new HashExpireCommand(null, fields, expiration, ExpirationOptions.none()); } /** - * Creates a new {@link ExpireCommand}. + * Creates a new {@link HashExpireCommand}. * * @param fields the {@code field} names to apply expiration to * @param ttl the unix point in time when to expire the given {@literal fields}. * @param precision can be {@link TimeUnit#SECONDS} or {@link TimeUnit#MILLISECONDS}. - * @return new instance of {@link ExpireCommand}. + * @return new instance of {@link HashExpireCommand}. */ - public static ExpireCommand expireAt(List fields, Instant ttl, TimeUnit precision) { + public static HashExpireCommand expireAt(List fields, Instant ttl, TimeUnit precision) { if (precision.compareTo(TimeUnit.MILLISECONDS) > 0) { return expire(fields, Expiration.unixTimestamp(ttl.getEpochSecond(), TimeUnit.SECONDS)); @@ -922,25 +921,25 @@ public static ExpireCommand expireAt(List fields, Instant ttl, TimeU /** * @param key the {@literal key} from which to expire the {@literal fields} from. - * @return new instance of {@link ExpireCommand}. + * @return new instance of {@link HashExpireCommand}. */ - public ExpireCommand from(ByteBuffer key) { - return new ExpireCommand(key, getFields(), expiration, options); + public HashExpireCommand from(ByteBuffer key) { + return new HashExpireCommand(key, getFields(), expiration, options); } /** * @param options additional options to be sent along with the command. - * @return new instance of {@link ExpireCommand}. + * @return new instance of {@link HashExpireCommand}. */ - public ExpireCommand withOptions(FieldExpirationOptions options) { - return new ExpireCommand(getKey(), getFields(), getExpiration(), options); + public HashExpireCommand withOptions(ExpirationOptions options) { + return new HashExpireCommand(getKey(), getFields(), getExpiration(), options); } public Expiration getExpiration() { return expiration; } - public FieldExpirationOptions getOptions() { + public ExpirationOptions getOptions() { return options; } } @@ -983,7 +982,7 @@ default Flux hExpire(ByteBuffer key, Duration duration, List f Assert.notNull(duration, "Duration must not be null"); - return applyExpiration(Flux.just(ExpireCommand.expire(fields, duration).from(key))) + return applyHashFieldExpiration(Flux.just(HashExpireCommand.expire(fields, duration).from(key))) .mapNotNull(NumericResponse::getOutput); } @@ -999,7 +998,7 @@ default Flux hExpire(ByteBuffer key, Duration duration, List f * @since 3.5 * @see Redis Documentation: HEXPIRE */ - Flux> applyExpiration(Publisher commands); + Flux> applyHashFieldExpiration(Publisher commands); /** * Expire a given {@literal field} after a given {@link Duration} of time, measured in milliseconds, has passed. @@ -1039,8 +1038,8 @@ default Flux hpExpire(ByteBuffer key, Duration duration, List Assert.notNull(duration, "Duration must not be null"); - return applyExpiration(Flux.just(new ExpireCommand(key, fields, - Expiration.from(duration.toMillis(), TimeUnit.MILLISECONDS), FieldExpirationOptions.none()))) + return applyHashFieldExpiration(Flux.just(new HashExpireCommand(key, fields, + Expiration.from(duration.toMillis(), TimeUnit.MILLISECONDS), ExpirationOptions.none()))) .mapNotNull(NumericResponse::getOutput); } @@ -1083,7 +1082,7 @@ default Flux hExpireAt(ByteBuffer key, Instant expireAt, List Assert.notNull(expireAt, "Duration must not be null"); - return applyExpiration(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.SECONDS).from(key))) + return applyHashFieldExpiration(Flux.just(HashExpireCommand.expireAt(fields, expireAt, TimeUnit.SECONDS).from(key))) .mapNotNull(NumericResponse::getOutput); } @@ -1126,7 +1125,8 @@ default Flux hpExpireAt(ByteBuffer key, Instant expireAt, List Assert.notNull(expireAt, "Duration must not be null"); - return applyExpiration(Flux.just(ExpireCommand.expireAt(fields, expireAt, TimeUnit.MILLISECONDS).from(key))) + return applyHashFieldExpiration( + Flux.just(HashExpireCommand.expireAt(fields, expireAt, TimeUnit.MILLISECONDS).from(key))) .mapNotNull(NumericResponse::getOutput); } diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java index 91b81640c1..62be2393ef 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java @@ -25,6 +25,7 @@ import java.util.List; import org.reactivestreams.Publisher; + import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand; @@ -32,6 +33,7 @@ import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.core.KeyScanOptions; import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -513,13 +515,35 @@ default Mono mUnlink(List keys) { */ class ExpireCommand extends KeyCommand { - private @Nullable Duration timeout; + private final Expiration expiration; + private final ExpirationOptions options; + + private ExpireCommand(ByteBuffer key, Duration timeout) { + this(key, Expiration.from(timeout), ExpirationOptions.none()); + } - private ExpireCommand(ByteBuffer key, @Nullable Duration timeout) { + private ExpireCommand(@Nullable ByteBuffer key, Expiration expiration, ExpirationOptions options) { super(key); - this.timeout = timeout; + this.expiration = expiration; + this.options = options; + } + + /** + * Creates a new {@link ExpireCommand} given a {@link ByteBuffer key} and {@link Expiration}. + * + * @param key must not be {@literal null}. + * @param expiration must not be {@literal null}. + * @return a new {@link ExpireCommand} for {@link ByteBuffer key} and {@link Expiration}. + * @since 3.5 + */ + public static ExpireCommand expire(ByteBuffer key, Expiration expiration) { + + Assert.notNull(key, "Key must not be null"); + Assert.notNull(expiration, "Expiration must not be null"); + + return new ExpireCommand(key, expiration, ExpirationOptions.none()); } /** @@ -532,7 +556,7 @@ public static ExpireCommand key(ByteBuffer key) { Assert.notNull(key, "Key must not be null"); - return new ExpireCommand(key, null); + return new ExpireCommand(key, Expiration.persistent(), ExpirationOptions.none()); } /** @@ -545,7 +569,21 @@ public ExpireCommand timeout(Duration timeout) { Assert.notNull(timeout, "Timeout must not be null"); - return new ExpireCommand(getKey(), timeout); + return new ExpireCommand(getKey(), Expiration.from(timeout), options); + } + + /** + * Applies the {@literal timeout}. Constructs a new command instance with all previously configured properties. + * + * @param timeout must not be {@literal null}. + * @return a new {@link ExpireCommand} with {@literal timeout} applied. + * @since 3.5 + */ + public ExpireCommand expire(Duration timeout) { + + Assert.notNull(timeout, "Timeout must not be null"); + + return new ExpireCommand(getKey(), Expiration.from(timeout), options); } /** @@ -553,10 +591,50 @@ public ExpireCommand timeout(Duration timeout) { */ @Nullable public Duration getTimeout() { - return timeout; + + if (expiration.isUnixTimestamp() || expiration.isPersistent()) { + return null; + } + + return Duration.ofMillis(expiration.getExpirationTimeInMilliseconds()); } + + /** + * @param options additional options to be sent along with the command. + * @return new instance of {@link ExpireCommand}. + * @since 3.5 + */ + public ExpireCommand withOptions(ExpirationOptions options) { + return new ExpireCommand(getKey(), getExpiration(), options); + } + + public Expiration getExpiration() { + return expiration; + } + + public ExpirationOptions getOptions() { + return options; + } + } + /** + * Expire a {@link List} of {@literal field} after a given {@link Duration} of time, measured in milliseconds, has + * passed. + * + * @param commands must not be {@literal null}. + * @return a {@link Flux} emitting the expiration results one by one, {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. + * @since 3.5 + * @see Redis Documentation: EXPIRE + * @see Redis Documentation: PEXPIRE + * @see Redis Documentation: EXPIREAT + * @see Redis Documentation: PEXPIREAT + * @see Redis Documentation: PERSIST + */ + Flux> applyExpiration(Publisher commands); + /** * Set time to live for given {@code key} in seconds. * @@ -581,7 +659,9 @@ default Mono expire(ByteBuffer key, Duration timeout) { * result. * @see Redis Documentation: EXPIRE */ - Flux> expire(Publisher commands); + default Flux> expire(Publisher commands) { + return applyExpiration(commands); + } /** * Set time to live for given {@code key} in milliseconds. @@ -607,7 +687,9 @@ default Mono pExpire(ByteBuffer key, Duration timeout) { * result. * @see Redis Documentation: PEXPIRE */ - Flux> pExpire(Publisher commands); + default Flux> pExpire(Publisher commands) { + return applyExpiration(commands); + } /** * {@code EXPIREAT}/{@code PEXPIREAT} command parameters. @@ -619,12 +701,18 @@ default Mono pExpire(ByteBuffer key, Duration timeout) { class ExpireAtCommand extends KeyCommand { private @Nullable Instant expireAt; + private final ExpirationOptions options; - private ExpireAtCommand(ByteBuffer key, @Nullable Instant expireAt) { + private ExpireAtCommand(ByteBuffer key, Instant expireAt) { + this(key, expireAt, ExpirationOptions.none()); + } + + private ExpireAtCommand(@Nullable ByteBuffer key, Instant expireAt, ExpirationOptions options) { super(key); this.expireAt = expireAt; + this.options = options; } /** @@ -653,6 +741,15 @@ public ExpireAtCommand timeout(Instant expireAt) { return new ExpireAtCommand(getKey(), expireAt); } + /** + * @param options additional options to be sent along with the command. + * @return new instance of {@link ExpireAtCommand}. + * @since 3.5 + */ + public ExpireAtCommand withOptions(ExpirationOptions options) { + return new ExpireAtCommand(getKey(), getExpireAt(), options); + } + /** * @return can be {@literal null}. */ @@ -660,6 +757,11 @@ public ExpireAtCommand timeout(Instant expireAt) { public Instant getExpireAt() { return expireAt; } + + public ExpirationOptions getOptions() { + return options; + } + } /** diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java index d038708526..cd99e4a516 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java @@ -21,7 +21,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; import org.springframework.lang.Nullable; @@ -257,7 +256,7 @@ public interface RedisHashCommands { /** * Apply a given {@link org.springframework.data.redis.core.types.Expiration} to the given {@literal fields}. - * + * * @param key must not be {@literal null}. * @param expiration the {@link org.springframework.data.redis.core.types.Expiration} to apply. * @param fields the names of the {@literal fields} to apply the {@literal expiration} to. @@ -267,9 +266,9 @@ public interface RedisHashCommands { * such field; * @since 3.5 */ - default @Nullable List applyExpiration(byte[] key, + default @Nullable List applyHashFieldExpiration(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, byte[]... fields) { - return applyExpiration(key, expiration, FieldExpirationOptions.none(), fields); + return applyHashFieldExpiration(key, expiration, ExpirationOptions.none(), fields); } /** @@ -284,14 +283,14 @@ public interface RedisHashCommands { * @since 3.5 */ @Nullable - default List applyExpiration(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, - FieldExpirationOptions options, byte[]... fields) { + default List applyHashFieldExpiration(byte[] key, + org.springframework.data.redis.core.types.Expiration expiration, ExpirationOptions options, byte[]... fields) { if (expiration.isPersistent()) { return hPersist(key, fields); } - if (ObjectUtils.nullSafeEquals(FieldExpirationOptions.none(), options)) { + if (ObjectUtils.nullSafeEquals(ExpirationOptions.none(), options)) { if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { if (expiration.isUnixTimestamp()) { return hpExpireAt(key, expiration.getExpirationTimeInMilliseconds(), fields); @@ -334,7 +333,7 @@ default List applyExpiration(byte[] key, org.springframework.data.redis.co */ @Nullable default List hExpire(byte[] key, long seconds, byte[]... fields) { - return hExpire(key, seconds, FieldExpirationOptions.Condition.ALWAYS, fields); + return hExpire(key, seconds, ExpirationOptions.Condition.ALWAYS, fields); } /** @@ -372,7 +371,7 @@ default List hExpire(byte[] key, Duration ttl, byte[]... fields) { * @since 3.5 */ @Nullable - List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields); + List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields); /** * Set time to live for given {@code fields} in milliseconds. @@ -390,7 +389,7 @@ default List hExpire(byte[] key, Duration ttl, byte[]... fields) { */ @Nullable default List hpExpire(byte[] key, long millis, byte[]... fields) { - return hpExpire(key, millis, FieldExpirationOptions.Condition.ALWAYS, fields); + return hpExpire(key, millis, ExpirationOptions.Condition.ALWAYS, fields); } /** @@ -429,7 +428,7 @@ default List hpExpire(byte[] key, Duration ttl, byte[]... fields) { * @since 3.5 */ @Nullable - List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields); + List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields); /** * Set the expiration for given {@code field} as a {@literal UNIX} timestamp. @@ -446,7 +445,7 @@ default List hpExpire(byte[] key, Duration ttl, byte[]... fields) { */ @Nullable default List hExpireAt(byte[] key, long unixTime, byte[]... fields) { - return hExpireAt(key, unixTime, FieldExpirationOptions.Condition.ALWAYS, fields); + return hExpireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS, fields); } /** @@ -465,7 +464,7 @@ default List hExpireAt(byte[] key, long unixTime, byte[]... fields) { * @since 3.5 */ @Nullable - List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields); + List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields); /** * Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds. @@ -482,7 +481,7 @@ default List hExpireAt(byte[] key, long unixTime, byte[]... fields) { */ @Nullable default List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... fields) { - return hpExpireAt(key, unixTimeInMillis, FieldExpirationOptions.Condition.ALWAYS, fields); + return hpExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS, fields); } /** @@ -501,7 +500,7 @@ default List hpExpireAt(byte[] key, long unixTimeInMillis, byte[]... field * @since 3.5 */ @Nullable - List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition, byte[]... fields); /** diff --git a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java index 49326637d3..4319dd8705 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java @@ -26,6 +26,7 @@ import org.springframework.data.redis.core.ScanOptions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Key-specific commands supported by Redis. @@ -181,23 +182,93 @@ default Cursor scan(KeyScanOptions options) { @Nullable Boolean renameNX(byte[] oldKey, byte[] newKey); + /** + * @param key must not be {@literal null}. + * @param expiration the {@link org.springframework.data.redis.core.types.Expiration} to apply. + * @param options additional options to be sent along with the command. + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. + * @since 3.5 + * @see Redis Documentation: EXPIRE + * @see Redis Documentation: PEXPIRE + * @see Redis Documentation: EXPIREAT + * @see Redis Documentation: PEXPIREAT + * @see Redis Documentation: PERSIST + */ + @Nullable + default Boolean applyExpiration(byte[] key, org.springframework.data.redis.core.types.Expiration expiration, + ExpirationOptions options) { + + if (expiration.isPersistent()) { + return persist(key); + } + + if (ObjectUtils.nullSafeEquals(ExpirationOptions.none(), options)) { + if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { + if (expiration.isUnixTimestamp()) { + return expireAt(key, expiration.getExpirationTimeInMilliseconds()); + } + return expire(key, expiration.getExpirationTimeInMilliseconds()); + } + if (expiration.isUnixTimestamp()) { + return expireAt(key, expiration.getExpirationTimeInSeconds()); + } + return expire(key, expiration.getExpirationTimeInSeconds()); + } + + if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) { + if (expiration.isUnixTimestamp()) { + return expireAt(key, expiration.getExpirationTimeInMilliseconds(), options.getCondition()); + } + + return expire(key, expiration.getExpirationTimeInMilliseconds(), options.getCondition()); + } + + if (expiration.isUnixTimestamp()) { + return expireAt(key, expiration.getExpirationTimeInSeconds(), options.getCondition()); + } + + return expire(key, expiration.getExpirationTimeInSeconds(), options.getCondition()); + } + /** * Set time to live for given {@code key} in seconds. * * @param key must not be {@literal null}. * @param seconds - * @return {@literal null} when used in pipeline / transaction. + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. + * @see Redis Documentation: EXPIRE + */ + @Nullable + default Boolean expire(byte[] key, long seconds) { + return expire(key, seconds, ExpirationOptions.Condition.ALWAYS); + } + + /** + * Set time to live for given {@code key} in seconds. + * + * @param key must not be {@literal null}. + * @param seconds + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. * @see Redis Documentation: EXPIRE + * @since 3.5 */ @Nullable - Boolean expire(byte[] key, long seconds); + Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition); /** * Set time to live for given {@code key} using {@link Duration#toSeconds() seconds} precision. * * @param key must not be {@literal null}. * @param duration - * @return {@literal null} when used in pipeline / transaction. + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. * @see Redis Documentation: EXPIRE * @since 3.5 */ @@ -211,18 +282,38 @@ default Boolean expire(byte[] key, Duration duration) { * * @param key must not be {@literal null}. * @param millis - * @return {@literal null} when used in pipeline / transaction. + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. + * @see Redis Documentation: PEXPIRE + */ + @Nullable + default Boolean pExpire(byte[] key, long millis) { + return pExpire(key, millis, ExpirationOptions.Condition.ALWAYS); + } + + /** + * Set time to live for given {@code key} in milliseconds. + * + * @param key must not be {@literal null}. + * @param millis + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. * @see Redis Documentation: PEXPIRE + * @since 3.5 */ @Nullable - Boolean pExpire(byte[] key, long millis); + Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition); /** * Set time to live for given {@code key} using {@link Duration#toMillis() milliseconds} precision. * * @param key must not be {@literal null}. * @param duration - * @return {@literal null} when used in pipeline / transaction. + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. * @see Redis Documentation: PEXPIRE * @since 3.5 */ @@ -236,11 +327,29 @@ default Boolean pExpire(byte[] key, Duration duration) { * * @param key must not be {@literal null}. * @param unixTime - * @return {@literal null} when used in pipeline / transaction. + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. + * @see Redis Documentation: EXPIREAT + */ + @Nullable + default Boolean expireAt(byte[] key, long unixTime) { + return expireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS); + } + + /** + * Set the expiration for given {@code key} as a {@literal UNIX} timestamp. + * + * @param key must not be {@literal null}. + * @param unixTime + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. * @see Redis Documentation: EXPIREAT + * @since 3.5 */ @Nullable - Boolean expireAt(byte[] key, long unixTime); + Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition); /** * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in {@link Instant#getEpochSecond() seconds} @@ -248,7 +357,9 @@ default Boolean pExpire(byte[] key, Duration duration) { * * @param key must not be {@literal null}. * @param unixTime - * @return {@literal null} when used in pipeline / transaction. + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. * @see Redis Documentation: EXPIREAT * @since 3.5 */ @@ -262,11 +373,29 @@ default Boolean expireAt(byte[] key, Instant unixTime) { * * @param key must not be {@literal null}. * @param unixTimeInMillis - * @return {@literal null} when used in pipeline / transaction. + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. + * @see Redis Documentation: PEXPIREAT + */ + @Nullable + default Boolean pExpireAt(byte[] key, long unixTimeInMillis) { + return pExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS); + } + + /** + * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds. + * + * @param key must not be {@literal null}. + * @param unixTimeInMillis + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. * @see Redis Documentation: PEXPIREAT + * @since 3.5 */ @Nullable - Boolean pExpireAt(byte[] key, long unixTimeInMillis); + Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition); /** * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in {@link Instant#toEpochMilli() @@ -274,7 +403,9 @@ default Boolean expireAt(byte[] key, Instant unixTime) { * * @param key must not be {@literal null}. * @param unixTime - * @return {@literal null} when used in pipeline / transaction. + * @return {@literal null} when used in pipeline / transaction. {@literal true} if the timeout was set or + * {@literal false} if the timeout was not set; for example, the key doesn't exist, or the operation was + * skipped because of the provided arguments. * @see Redis Documentation: PEXPIREAT * @since 3.5 */ diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java index 1069e430c8..dfcc585130 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -224,7 +224,22 @@ interface StringTuple extends Tuple { * @see Redis Documentation: EXPIRE * @see RedisKeyCommands#expire(byte[], long) */ - Boolean expire(String key, long seconds); + default Boolean expire(String key, long seconds) { + return expire(key, seconds, ExpirationOptions.Condition.ALWAYS); + } + + /** + * Set time to live for given {@code key} in seconds. + * + * @param key must not be {@literal null}. + * @param condition the condition for expiration, must not be {@literal null}. + * @param seconds + * @return + * @since 3.5 + * @see Redis Documentation: EXPIRE + * @see RedisKeyCommands#expire(byte[], long) + */ + Boolean expire(String key, long seconds, ExpirationOptions.Condition condition); /** * Set time to live for given {@code key} in milliseconds. @@ -235,7 +250,22 @@ interface StringTuple extends Tuple { * @see Redis Documentation: PEXPIRE * @see RedisKeyCommands#pExpire(byte[], long) */ - Boolean pExpire(String key, long millis); + default Boolean pExpire(String key, long millis) { + return pExpire(key, millis, ExpirationOptions.Condition.ALWAYS); + } + + /** + * Set time to live for given {@code key} in milliseconds. + * + * @param key must not be {@literal null}. + * @param millis + * @param condition the condition for expiration, must not be {@literal null}. + * @return + * @since 3.5 + * @see Redis Documentation: PEXPIRE + * @see RedisKeyCommands#pExpire(byte[], long) + */ + Boolean pExpire(String key, long millis, ExpirationOptions.Condition condition); /** * Set the expiration for given {@code key} as a {@literal UNIX} timestamp. @@ -246,7 +276,22 @@ interface StringTuple extends Tuple { * @see Redis Documentation: EXPIREAT * @see RedisKeyCommands#expireAt(byte[], long) */ - Boolean expireAt(String key, long unixTime); + default Boolean expireAt(String key, long unixTime) { + return expireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS); + } + + /** + * Set the expiration for given {@code key} as a {@literal UNIX} timestamp. + * + * @param key must not be {@literal null}. + * @param unixTime + * @param condition the condition for expiration, must not be {@literal null}. + * @return + * @since 3.5 + * @see Redis Documentation: EXPIREAT + * @see RedisKeyCommands#expireAt(byte[], long) + */ + Boolean expireAt(String key, long unixTime, ExpirationOptions.Condition condition); /** * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds. @@ -257,7 +302,22 @@ interface StringTuple extends Tuple { * @see Redis Documentation: PEXPIREAT * @see RedisKeyCommands#pExpireAt(byte[], long) */ - Boolean pExpireAt(String key, long unixTimeInMillis); + default Boolean pExpireAt(String key, long unixTimeInMillis) { + return pExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS); + } + + /** + * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds. + * + * @param key must not be {@literal null}. + * @param unixTimeInMillis + * @param condition the condition for expiration, must not be {@literal null}. + * @return + * @since 3.5 + * @see Redis Documentation: PEXPIREAT + * @see RedisKeyCommands#pExpireAt(byte[], long) + */ + Boolean pExpireAt(String key, long unixTimeInMillis, ExpirationOptions.Condition condition); /** * Remove the expiration from given {@code key}. @@ -2348,7 +2408,7 @@ Long zRangeStoreRevByScore(String dstKey, String srcKey, */ @Nullable default List hExpire(String key, long seconds, String... fields) { - return hExpire(key, seconds, Hash.FieldExpirationOptions.Condition.ALWAYS, fields); + return hExpire(key, seconds, ExpirationOptions.Condition.ALWAYS, fields); } /** @@ -2366,7 +2426,7 @@ default List hExpire(String key, long seconds, String... fields) { * @since 3.5 */ @Nullable - List hExpire(String key, long seconds, Hash.FieldExpirationOptions.Condition condition, String... fields); + List hExpire(String key, long seconds, ExpirationOptions.Condition condition, String... fields); /** * Set time to live for given {@code field} in milliseconds. @@ -2383,7 +2443,7 @@ default List hExpire(String key, long seconds, String... fields) { */ @Nullable default List hpExpire(String key, long millis, String... fields) { - return hpExpire(key, millis, Hash.FieldExpirationOptions.Condition.ALWAYS, fields); + return hpExpire(key, millis, ExpirationOptions.Condition.ALWAYS, fields); } /** @@ -2401,7 +2461,7 @@ default List hpExpire(String key, long millis, String... fields) { * @since 3.5 */ @Nullable - List hpExpire(String key, long millis, Hash.FieldExpirationOptions.Condition condition, String... fields); + List hpExpire(String key, long millis, ExpirationOptions.Condition condition, String... fields); /** * Set the expiration for given {@code field} as a {@literal UNIX} timestamp. @@ -2418,7 +2478,7 @@ default List hpExpire(String key, long millis, String... fields) { */ @Nullable default List hExpireAt(String key, long unixTime, String... fields) { - return hExpireAt(key, unixTime, Hash.FieldExpirationOptions.Condition.ALWAYS, fields); + return hExpireAt(key, unixTime, ExpirationOptions.Condition.ALWAYS, fields); } /** @@ -2436,7 +2496,7 @@ default List hExpireAt(String key, long unixTime, String... fields) { * @since 3.5 */ @Nullable - List hExpireAt(String key, long unixTime, Hash.FieldExpirationOptions.Condition condition, String... fields); + List hExpireAt(String key, long unixTime, ExpirationOptions.Condition condition, String... fields); /** * Set the expiration for given {@code field} as a {@literal UNIX} timestamp in milliseconds. @@ -2453,7 +2513,7 @@ default List hExpireAt(String key, long unixTime, String... fields) { */ @Nullable default List hpExpireAt(String key, long unixTimeInMillis, String... fields) { - return hpExpireAt(key, unixTimeInMillis, Hash.FieldExpirationOptions.Condition.ALWAYS, fields); + return hpExpireAt(key, unixTimeInMillis, ExpirationOptions.Condition.ALWAYS, fields); } /** @@ -2471,7 +2531,7 @@ default List hpExpireAt(String key, long unixTimeInMillis, String... field * @since 3.5 */ @Nullable - List hpExpireAt(String key, long unixTimeInMillis, Hash.FieldExpirationOptions.Condition condition, + List hpExpireAt(String key, long unixTimeInMillis, ExpirationOptions.Condition condition, String... fields); /** diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java index 1223ab4c06..551a17d153 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java @@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit; import org.springframework.dao.DataAccessException; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisHashCommands; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanCursor; @@ -291,13 +291,13 @@ protected ScanIteration> doScan(CursorId cursorId, ScanOpt } @Override - public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) { Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); try { - if (condition == FieldExpirationOptions.Condition.ALWAYS) { + if (condition == ExpirationOptions.Condition.ALWAYS) { return connection.getCluster().hexpire(key, seconds, fields); } @@ -308,13 +308,13 @@ public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condi } @Override - public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) { Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); try { - if (condition == FieldExpirationOptions.Condition.ALWAYS) { + if (condition == ExpirationOptions.Condition.ALWAYS) { return connection.getCluster().hpexpire(key, millis, fields); } @@ -325,13 +325,13 @@ public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condi } @Override - public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) { Assert.notNull(key, "Key must not be null"); Assert.notNull(fields, "Fields must not be null"); try { - if (condition == FieldExpirationOptions.Condition.ALWAYS) { + if (condition == ExpirationOptions.Condition.ALWAYS) { return connection.getCluster().hexpireAt(key, unixTime, fields); } @@ -342,7 +342,7 @@ public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Co } @Override - public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + public List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition, byte[]... fields) { Assert.notNull(key, "Key must not be null"); @@ -350,7 +350,7 @@ public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationO try { - if (condition == FieldExpirationOptions.Condition.ALWAYS) { + if (condition == ExpirationOptions.Condition.ALWAYS) { return connection.getCluster().hpexpireAt(key, unixTimeInMillis, fields); } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java index cb1b07e9c3..559c15bf86 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java @@ -16,6 +16,7 @@ package org.springframework.data.redis.connection.jedis; import redis.clients.jedis.Jedis; +import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; @@ -35,6 +36,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.ClusterSlotHashUtil; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisClusterNode; import org.springframework.data.redis.connection.RedisKeyCommands; import org.springframework.data.redis.connection.RedisNode; @@ -274,48 +276,67 @@ public Boolean renameNX(byte[] sourceKey, byte[] targetKey) { } @Override - public Boolean expire(byte[] key, long seconds) { + public Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); try { - return JedisConverters.toBoolean(connection.getCluster().expire(key, seconds)); + if (condition == ExpirationOptions.Condition.ALWAYS) { + return JedisConverters.toBoolean(connection.getCluster().expire(key, seconds)); + } + + return JedisConverters + .toBoolean(connection.getCluster().expire(key, seconds, ExpiryOption.valueOf(condition.name()))); } catch (Exception ex) { throw convertJedisAccessException(ex); } } @Override - public Boolean pExpire(byte[] key, long millis) { + public Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); try { - return JedisConverters.toBoolean(connection.getCluster().pexpire(key, millis)); + if (condition == ExpirationOptions.Condition.ALWAYS) { + return JedisConverters.toBoolean(connection.getCluster().pexpire(key, millis)); + } + return JedisConverters + .toBoolean(connection.getCluster().pexpire(key, millis, ExpiryOption.valueOf(condition.name()))); } catch (Exception ex) { throw convertJedisAccessException(ex); } } @Override - public Boolean expireAt(byte[] key, long unixTime) { + public Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); try { - return JedisConverters.toBoolean(connection.getCluster().expireAt(key, unixTime)); + if (condition == ExpirationOptions.Condition.ALWAYS) { + return JedisConverters.toBoolean(connection.getCluster().expireAt(key, unixTime)); + } + + return JedisConverters + .toBoolean(connection.getCluster().expireAt(key, unixTime, ExpiryOption.valueOf(condition.name()))); } catch (Exception ex) { throw convertJedisAccessException(ex); } } @Override - public Boolean pExpireAt(byte[] key, long unixTimeInMillis) { + public Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); try { - return JedisConverters.toBoolean(connection.getCluster().pexpireAt(key, unixTimeInMillis)); + if (condition == ExpirationOptions.Condition.ALWAYS) { + return JedisConverters.toBoolean(connection.getCluster().pexpireAt(key, unixTimeInMillis)); + } + + return JedisConverters + .toBoolean(connection.getCluster().pexpireAt(key, unixTimeInMillis, ExpiryOption.valueOf(condition.name()))); } catch (Exception ex) { throw convertJedisAccessException(ex); } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java index 2e83d8aba0..33189c27b4 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java @@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisHashCommands; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.core.Cursor; @@ -256,9 +256,9 @@ protected void doClose() { } @Override - public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) { - if (condition == FieldExpirationOptions.Condition.ALWAYS) { + if (condition == ExpirationOptions.Condition.ALWAYS) { return connection.invoke().just(Jedis::hexpire, PipelineBinaryCommands::hexpire, key, seconds, fields); } @@ -267,9 +267,9 @@ public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condi } @Override - public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) { - if (condition == FieldExpirationOptions.Condition.ALWAYS) { + if (condition == ExpirationOptions.Condition.ALWAYS) { return connection.invoke().just(Jedis::hpexpire, PipelineBinaryCommands::hpexpire, key, millis, fields); } @@ -278,9 +278,9 @@ public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condi } @Override - public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) { - if (condition == FieldExpirationOptions.Condition.ALWAYS) { + if (condition == ExpirationOptions.Condition.ALWAYS) { return connection.invoke().just(Jedis::hexpireAt, PipelineBinaryCommands::hexpireAt, key, unixTime, fields); } @@ -289,10 +289,10 @@ public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Co } @Override - public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + public List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition, byte[]... fields) { - if (condition == FieldExpirationOptions.Condition.ALWAYS) { + if (condition == ExpirationOptions.Condition.ALWAYS) { return connection.invoke().just(Jedis::hpexpireAt, PipelineBinaryCommands::hpexpireAt, key, unixTimeInMillis, fields); } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java index 93f0ddfff6..9d4465b410 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.connection.jedis; +import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.commands.JedisBinaryCommands; import redis.clients.jedis.commands.PipelineBinaryCommands; import redis.clients.jedis.params.RestoreParams; @@ -30,6 +31,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisKeyCommands; import org.springframework.data.redis.connection.SortParameters; import org.springframework.data.redis.connection.ValueEncoding; @@ -206,43 +208,69 @@ public Boolean renameNX(byte[] sourceKey, byte[] targetKey) { } @Override - public Boolean expire(byte[] key, long seconds) { + public Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); if (seconds > Integer.MAX_VALUE) { - return pExpire(key, TimeUnit.SECONDS.toMillis(seconds)); + return pExpire(key, TimeUnit.SECONDS.toMillis(seconds), condition); } - return connection.invoke().from(JedisBinaryCommands::expire, PipelineBinaryCommands::expire, key, seconds) + if (condition == ExpirationOptions.Condition.ALWAYS) { + return connection.invoke().from(JedisBinaryCommands::expire, PipelineBinaryCommands::expire, key, seconds) + .get(JedisConverters.longToBoolean()); + } + + ExpiryOption option = ExpiryOption.valueOf(condition.name()); + return connection.invoke().from(JedisBinaryCommands::expire, PipelineBinaryCommands::expire, key, seconds, option) .get(JedisConverters.longToBoolean()); } @Override - public Boolean pExpire(byte[] key, long millis) { + public Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); - return connection.invoke().from(JedisBinaryCommands::pexpire, PipelineBinaryCommands::pexpire, key, millis) + if (condition == ExpirationOptions.Condition.ALWAYS) { + return connection.invoke().from(JedisBinaryCommands::pexpire, PipelineBinaryCommands::pexpire, key, millis) + .get(JedisConverters.longToBoolean()); + } + + ExpiryOption option = ExpiryOption.valueOf(condition.name()); + return connection.invoke().from(JedisBinaryCommands::pexpire, PipelineBinaryCommands::pexpire, key, millis, option) .get(JedisConverters.longToBoolean()); } @Override - public Boolean expireAt(byte[] key, long unixTime) { + public Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); - return connection.invoke().from(JedisBinaryCommands::expireAt, PipelineBinaryCommands::expireAt, key, unixTime) + if (condition == ExpirationOptions.Condition.ALWAYS) { + return connection.invoke().from(JedisBinaryCommands::expireAt, PipelineBinaryCommands::expireAt, key, unixTime) + .get(JedisConverters.longToBoolean()); + } + + ExpiryOption option = ExpiryOption.valueOf(condition.name()); + return connection.invoke() + .from(JedisBinaryCommands::expireAt, PipelineBinaryCommands::expireAt, key, unixTime, option) .get(JedisConverters.longToBoolean()); } @Override - public Boolean pExpireAt(byte[] key, long unixTimeInMillis) { + public Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); + if (condition == ExpirationOptions.Condition.ALWAYS) { + return connection.invoke() + .from(JedisBinaryCommands::pexpireAt, PipelineBinaryCommands::pexpireAt, key, unixTimeInMillis) + .get(JedisConverters.longToBoolean()); + } + + ExpiryOption option = ExpiryOption.valueOf(condition.name()); return connection.invoke() - .from(JedisBinaryCommands::pexpireAt, PipelineBinaryCommands::pexpireAt, key, unixTimeInMillis) + .from(JedisBinaryCommands::pexpireAt, PipelineBinaryCommands::pexpireAt, key, unixTimeInMillis, option) .get(JedisConverters.longToBoolean()); } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java index 032d6230d6..278671704b 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java @@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisHashCommands; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.core.Cursor; @@ -215,25 +215,25 @@ public Cursor> hScan(byte[] key, ScanOptions options) { } @Override - public List hExpire(byte[] key, long seconds, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) { return connection.invoke().fromMany(RedisHashAsyncCommands::hexpire, key, seconds, getExpireArgs(condition), fields) .toList(); } @Override - public List hpExpire(byte[] key, long millis, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) { return connection.invoke().fromMany(RedisHashAsyncCommands::hpexpire, key, millis, getExpireArgs(condition), fields) .toList(); } @Override - public List hExpireAt(byte[] key, long unixTime, FieldExpirationOptions.Condition condition, byte[]... fields) { + public List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) { return connection.invoke() .fromMany(RedisHashAsyncCommands::hexpireat, key, unixTime, getExpireArgs(condition), fields).toList(); } @Override - public List hpExpireAt(byte[] key, long unixTimeInMillis, FieldExpirationOptions.Condition condition, + public List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition, byte[]... fields) { return connection.invoke() .fromMany(RedisHashAsyncCommands::hpexpireat, key, unixTimeInMillis, getExpireArgs(condition), fields).toList(); @@ -314,13 +314,13 @@ private static Entry toEntry(KeyValue value) { return value.hasValue() ? Converters.entryOf(value.getKey(), value.getValue()) : null; } - private ExpireArgs getExpireArgs(FieldExpirationOptions.Condition condition) { + private static ExpireArgs getExpireArgs(ExpirationOptions.Condition condition) { return new ExpireArgs() { @Override public void build(CommandArgs args) { - if (ObjectUtils.nullSafeEquals(condition, FieldExpirationOptions.Condition.ALWAYS)) { + if (ObjectUtils.nullSafeEquals(condition, ExpirationOptions.Condition.ALWAYS)) { return; } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java index a9514cd793..78d4e7006e 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java @@ -16,12 +16,14 @@ package org.springframework.data.redis.connection.lettuce; import io.lettuce.core.CopyArgs; +import io.lettuce.core.ExpireArgs; import io.lettuce.core.KeyScanCursor; import io.lettuce.core.RestoreArgs; import io.lettuce.core.ScanArgs; import io.lettuce.core.ScanCursor; import io.lettuce.core.SortArgs; import io.lettuce.core.api.async.RedisKeyAsyncCommands; +import io.lettuce.core.protocol.CommandArgs; import java.time.Duration; import java.util.List; @@ -30,6 +32,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisKeyCommands; import org.springframework.data.redis.connection.SortParameters; import org.springframework.data.redis.connection.ValueEncoding; @@ -39,6 +42,7 @@ import org.springframework.data.redis.core.ScanOptions; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * @author Christoph Strobl @@ -192,35 +196,35 @@ public Boolean renameNX(byte[] sourceKey, byte[] targetKey) { } @Override - public Boolean expire(byte[] key, long seconds) { + public Boolean expire(byte[] key, long seconds, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); - return connection.invoke().just(RedisKeyAsyncCommands::expire, key, seconds); + return connection.invoke().just(RedisKeyAsyncCommands::expire, key, seconds, getExpireArgs(condition)); } @Override - public Boolean pExpire(byte[] key, long millis) { + public Boolean pExpire(byte[] key, long millis, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); - return connection.invoke().just(RedisKeyAsyncCommands::pexpire, key, millis); + return connection.invoke().just(RedisKeyAsyncCommands::pexpire, key, millis, getExpireArgs(condition)); } @Override - public Boolean expireAt(byte[] key, long unixTime) { + public Boolean expireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); - return connection.invoke().just(RedisKeyAsyncCommands::expireat, key, unixTime); + return connection.invoke().just(RedisKeyAsyncCommands::expireat, key, unixTime, getExpireArgs(condition)); } @Override - public Boolean pExpireAt(byte[] key, long unixTimeInMillis) { + public Boolean pExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition) { Assert.notNull(key, "Key must not be null"); - return connection.invoke().just(RedisKeyAsyncCommands::pexpireat, key, unixTimeInMillis); + return connection.invoke().just(RedisKeyAsyncCommands::pexpireat, key, unixTimeInMillis, getExpireArgs(condition)); } @Override @@ -337,4 +341,19 @@ public Long refcount(byte[] key) { return connection.invoke().just(RedisKeyAsyncCommands::objectRefcount, key); } + + private static ExpireArgs getExpireArgs(ExpirationOptions.Condition condition) { + + return new ExpireArgs() { + @Override + public void build(CommandArgs args) { + + if (ObjectUtils.nullSafeEquals(condition, ExpirationOptions.Condition.ALWAYS)) { + return; + } + + args.add(condition.name()); + } + }; + } } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java index 84dd2ca906..e637296219 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java @@ -31,7 +31,7 @@ import java.util.stream.Collectors; import org.reactivestreams.Publisher; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.ReactiveHashCommands; import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; @@ -269,7 +269,8 @@ public Flux> hStrLen(Publisher> applyExpiration(Publisher commands) { + public Flux> applyHashFieldExpiration( + Publisher commands) { return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { @@ -287,7 +288,7 @@ public Flux> applyExpiration(Publisher void build(CommandArgs args) { super.build(args); - if (ObjectUtils.nullSafeEquals(command.getOptions(), FieldExpirationOptions.none())) { + if (ObjectUtils.nullSafeEquals(command.getOptions(), ExpirationOptions.none())) { return; } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java index a1371b7856..3dbb44c69d 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java @@ -16,8 +16,10 @@ package org.springframework.data.redis.connection.lettuce; import io.lettuce.core.CopyArgs; +import io.lettuce.core.ExpireArgs; import io.lettuce.core.ScanStream; import io.lettuce.core.api.reactive.RedisKeyReactiveCommands; +import io.lettuce.core.protocol.CommandArgs; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -25,9 +27,12 @@ import java.time.Duration; import java.util.Collection; import java.util.List; +import java.util.concurrent.TimeUnit; import org.reactivestreams.Publisher; + import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.ReactiveKeyCommands; import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; @@ -38,6 +43,7 @@ import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding; import org.springframework.data.redis.core.ScanOptions; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * @author Christoph Strobl @@ -206,27 +212,45 @@ public Flux, Long>> mUnlink(Publisher> expire(Publisher commands) { + public Flux> applyExpiration(Publisher commands) { return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKey(), "Key must not be null"); - Assert.notNull(command.getTimeout(), "Timeout must not be null"); - return cmd.expire(command.getKey(), command.getTimeout().getSeconds()) - .map(value -> new BooleanResponse<>(command, value)); - })); - } + if (command.getExpiration().isPersistent()) { + return cmd.persist(command.getKey()).map(value -> new BooleanResponse<>(command, value)); + } - @Override - public Flux> pExpire(Publisher commands) { + ExpireArgs args = new ExpireArgs() { - return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + @Override + public void build(CommandArgs args) { + super.build(args); + if (ObjectUtils.nullSafeEquals(command.getOptions(), ExpirationOptions.none())) { + return; + } - Assert.notNull(command.getKey(), "Key must not be null"); - Assert.notNull(command.getTimeout(), "Timeout must not be null"); + args.add(command.getOptions().getCondition().name()); + } + }; + + if (command.getExpiration().isUnixTimestamp()) { + + if (command.getExpiration().getTimeUnit().equals(TimeUnit.MILLISECONDS)) { + return cmd.pexpireat(command.getKey(), command.getExpiration().getExpirationTimeInMilliseconds(), args) + .map(value -> new BooleanResponse<>(command, value)); + } + return cmd.expireat(command.getKey(), command.getExpiration().getExpirationTimeInSeconds(), args) + .map(value -> new BooleanResponse<>(command, value)); + } + + if (command.getExpiration().getTimeUnit().equals(TimeUnit.MILLISECONDS)) { + return cmd.pexpire(command.getKey(), command.getExpiration().getExpirationTimeInMilliseconds(), args) + .map(value -> new BooleanResponse<>(command, value)); + } - return cmd.pexpire(command.getKey(), command.getTimeout().toMillis()) + return cmd.expire(command.getKey(), command.getExpiration().getExpirationTimeInSeconds(), args) .map(value -> new BooleanResponse<>(command, value)); })); } @@ -239,7 +263,7 @@ public Flux> expireAt(Publisher new BooleanResponse<>(command, value)); })); } @@ -252,7 +276,7 @@ public Flux> pExpireAt(Publisher new BooleanResponse<>(command, value)); })); } @@ -319,4 +343,21 @@ public Mono idletime(ByteBuffer key) { public Mono refcount(ByteBuffer key) { return connection.execute(cmd -> cmd.objectRefcount(key)).next(); } + + private static ExpireArgs getExpireArgs(ExpirationOptions options) { + + return new ExpireArgs() { + + @Override + public void build(CommandArgs args) { + super.build(args); + if (ObjectUtils.nullSafeEquals(options.getCondition(), ExpirationOptions.Condition.ALWAYS)) { + return; + } + + args.add(options.getCondition().name()); + } + }; + } + } diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java index d49041a66d..8546445af4 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundHashFieldExpirationOperations.java @@ -19,7 +19,7 @@ import java.time.Instant; import java.util.concurrent.TimeUnit; -import org.springframework.data.redis.connection.Hash; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.Expirations; import org.springframework.lang.Nullable; @@ -40,18 +40,17 @@ public interface BoundHashFieldExpirationOperations { * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. */ default ExpireChanges expire(Expiration expiration) { - return expire(expiration, Hash.FieldExpirationOptions.none()); + return expire(expiration, ExpirationOptions.none()); } /** - * Apply {@link Expiration} to the bound hash key/hash fields given {@link Hash.FieldExpirationOptions expiration - * options}. + * Apply {@link Expiration} to the bound hash key/hash fields given {@link ExpirationOptions expiration options}. * * @param expiration the expiration definition. * @param options expiration options. * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. */ - ExpireChanges expire(Expiration expiration, Hash.FieldExpirationOptions options); + ExpireChanges expire(Expiration expiration, ExpirationOptions options); /** * Set time to live for the bound hash key/hash fields. diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java index 0d287e929f..0355588ea4 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java @@ -223,7 +223,7 @@ public interface BoundHashOperations extends BoundKeyOperations { * @return the bound operations object to perform operations on the hash field expiration. * @since 3.5 */ - default BoundHashFieldExpirationOperations expiration() { + default BoundHashFieldExpirationOperations hashExpiration() { return new DefaultBoundHashFieldExpirationOperations<>(getOperations().opsForHash(), getKey(), this::keys); } @@ -235,8 +235,8 @@ default BoundHashFieldExpirationOperations expiration() { * @return the bound operations object to perform operations on the hash field expiration. * @since 3.5 */ - default BoundHashFieldExpirationOperations expiration(HK... hashFields) { - return expiration(Arrays.asList(hashFields)); + default BoundHashFieldExpirationOperations hashExpiration(HK... hashFields) { + return hashExpiration(Arrays.asList(hashFields)); } /** @@ -247,7 +247,7 @@ default BoundHashFieldExpirationOperations expiration(HK... hashFields) { * @return the bound operations object to perform operations on the hash field expiration. * @since 3.5 */ - default BoundHashFieldExpirationOperations expiration(Collection hashFields) { + default BoundHashFieldExpirationOperations hashExpiration(Collection hashFields) { return new DefaultBoundHashFieldExpirationOperations<>(getOperations().opsForHash(), getKey(), () -> hashFields); } diff --git a/src/main/java/org/springframework/data/redis/core/DefaultBoundHashFieldExpirationOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultBoundHashFieldExpirationOperations.java index 8dbabe6dd5..fdb45c57bc 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultBoundHashFieldExpirationOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultBoundHashFieldExpirationOperations.java @@ -21,7 +21,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import org.springframework.data.redis.connection.Hash; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.Expirations; import org.springframework.lang.Nullable; @@ -48,7 +48,7 @@ public DefaultBoundHashFieldExpirationOperations(HashOperations operat } @Override - public ExpireChanges expire(Expiration expiration, Hash.FieldExpirationOptions options) { + public ExpireChanges expire(Expiration expiration, ExpirationOptions options) { return operations.expire(key, expiration, options, getHashKeys()); } diff --git a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java index 804617616f..ccdfbee704 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java @@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit; import org.springframework.core.convert.converter.Converter; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.Expirations; @@ -251,13 +251,13 @@ public ExpireChanges expireAt(K key, Instant instant, Collection hashKey } @Override - public ExpireChanges expire(K key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys) { + public ExpireChanges expire(K key, Expiration expiration, ExpirationOptions options, Collection hashKeys) { List orderedKeys = List.copyOf(hashKeys); byte[] rawKey = rawKey(key); byte[][] rawHashKeys = rawHashKeys(orderedKeys.toArray()); List raw = execute( - connection -> connection.hashCommands().applyExpiration(rawKey, expiration, options, rawHashKeys)); + connection -> connection.hashCommands().applyHashFieldExpiration(rawKey, expiration, options, rawHashKeys)); return raw != null ? ExpireChanges.of(orderedKeys, raw) : null; } diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java index 540b351778..282f229913 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java @@ -30,9 +30,9 @@ import org.reactivestreams.Publisher; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.ReactiveHashCommands; -import org.springframework.data.redis.connection.ReactiveHashCommands.ExpireCommand; +import org.springframework.data.redis.connection.ReactiveHashCommands.HashExpireCommand; import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.core.types.Expiration; @@ -244,11 +244,11 @@ public Flux> scan(H key, ScanOptions options) { @Override public Mono> expire(H key, Duration timeout, Collection hashKeys) { - return expire(key, Expiration.from(timeout), FieldExpirationOptions.none(), hashKeys); + return expire(key, Expiration.from(timeout), ExpirationOptions.none(), hashKeys); } @Override - public Mono> expire(H key, Expiration expiration, FieldExpirationOptions options, + public Mono> expire(H key, Expiration expiration, ExpirationOptions options, Collection hashKeys) { List orderedKeys = List.copyOf(hashKeys); @@ -257,7 +257,8 @@ public Mono> expire(H key, Expiration expiration, FieldExpirat Mono> raw = createFlux(connection -> { return connection - .applyExpiration(Mono.just(ExpireCommand.expire(rawHashKeys, expiration).from(rawKey).withOptions(options))) + .applyHashFieldExpiration( + Mono.just(HashExpireCommand.expire(rawHashKeys, expiration).from(rawKey).withOptions(options))) .map(NumericResponse::getOutput); }).collectList(); diff --git a/src/main/java/org/springframework/data/redis/core/ExpireChanges.java b/src/main/java/org/springframework/data/redis/core/ExpireChanges.java index 029922d96e..10bef1e2d2 100644 --- a/src/main/java/org/springframework/data/redis/core/ExpireChanges.java +++ b/src/main/java/org/springframework/data/redis/core/ExpireChanges.java @@ -151,6 +151,10 @@ public record ExpiryChangeState(long value) { public static final ExpiryChangeState OK = new ExpiryChangeState(1L); public static final ExpiryChangeState EXPIRED = new ExpiryChangeState(2L); + static ExpiryChangeState of(boolean value) { + return value ? OK : CONDITION_NOT_MET; + } + static ExpiryChangeState of(Number value) { return switch (value.intValue()) { case -2 -> DOES_NOT_EXIST; diff --git a/src/main/java/org/springframework/data/redis/core/HashOperations.java b/src/main/java/org/springframework/data/redis/core/HashOperations.java index f57143c737..1dc34db4c3 100644 --- a/src/main/java/org/springframework/data/redis/core/HashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/HashOperations.java @@ -24,7 +24,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.Expirations; import org.springframework.lang.Nullable; @@ -258,7 +258,7 @@ public interface HashOperations { ExpireChanges expireAt(H key, Instant expireAt, Collection hashKeys); /** - * Apply the expiration for given {@code hashKeys} as a {@literal date} timestamp. + * Apply the expiration for given {@code hashKeys}. * * @param key must not be {@literal null}. * @param expiration must not be {@literal null}. @@ -266,11 +266,15 @@ public interface HashOperations { * @param hashKeys must not be {@literal null}. * @return changes to the hash fields. {@literal null} when used in pipeline / transaction. * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. - * @see Redis Documentation: HEXPIRE + * @see Redis Documentation: HEXPIRE + * @see Redis Documentation: HPEXPIRE + * @see Redis Documentation: HEXPIREAT + * @see Redis Documentation: HPEXPIREAT + * @see Redis Documentation: HPERSIST * @since 3.5 */ @Nullable - ExpireChanges expire(H key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys); + ExpireChanges expire(H key, Expiration expiration, ExpirationOptions options, Collection hashKeys); /** * Remove the expiration from given {@code hashKeys} . diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java index 2fe3b0c65b..4c8c0986d8 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java @@ -26,7 +26,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.Expirations; import org.springframework.lang.Nullable; @@ -263,7 +263,7 @@ default Flux> scan(H key) { * @see Redis Documentation: HEXPIRE * @since 3.5 */ - Mono> expire(H key, Expiration expiration, FieldExpirationOptions options, Collection hashKeys); + Mono> expire(H key, Expiration expiration, ExpirationOptions options, Collection hashKeys); /** * Set the expiration for given {@code hashKey} as a {@literal date} timestamp. diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java index f027c46366..46faa18071 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java @@ -26,9 +26,12 @@ import java.util.List; import org.reactivestreams.Publisher; + import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.ReactiveSubscription.Message; import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.hash.HashMapper; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.PatternTopic; @@ -373,6 +376,22 @@ default Flux scan() { */ Mono expireAt(K key, Instant expireAt); + /** + * Set the expiration for given {@code key}. + * + * @param key must not be {@literal null}. + * @param expiration must not be {@literal null}. + * @param options must not be {@literal null}. + * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @see Redis Documentation: EXPIRE + * @see Redis Documentation: PEXPIRE + * @see Redis Documentation: EXPIREAT + * @see Redis Documentation: PEXPIREAT + * @see Redis Documentation: PERSIST + * @since 3.5 + */ + Mono expire(K key, Expiration expiration, ExpirationOptions options); + /** * Remove the expiration from given {@code key}. * diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java index 92792ed81b..0649072b4b 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java @@ -29,6 +29,8 @@ import org.reactivestreams.Publisher; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; +import org.springframework.data.redis.connection.ReactiveKeyCommands; import org.springframework.data.redis.connection.ReactiveRedisConnection; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; @@ -36,6 +38,7 @@ import org.springframework.data.redis.core.script.DefaultReactiveScriptExecutor; import org.springframework.data.redis.core.script.ReactiveScriptExecutor; import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.hash.HashMapper; import org.springframework.data.redis.hash.ObjectHashMapper; import org.springframework.data.redis.listener.ReactiveRedisMessageListenerContainer; @@ -454,6 +457,19 @@ public Mono expireAt(K key, Instant expireAt) { return doCreateMono(connection -> connection.keyCommands().pExpireAt(rawKey(key), expireAt)); } + @Override + public Mono expire(K key, Expiration expiration, ExpirationOptions options) { + + Assert.notNull(key, "Key must not be null"); + Assert.notNull(expiration, "Expiration at must not be null"); + Assert.notNull(options, "ExpirationOptions at must not be null"); + + Mono just = Mono + .just(ReactiveKeyCommands.ExpireCommand.expire(rawKey(key), expiration).withOptions(options)); + return doCreateMono(connection -> connection.keyCommands().applyExpiration(just)) + .map(ReactiveRedisConnection.BooleanResponse::getOutput).map(ExpireChanges.ExpiryChangeState::of); + } + @Override public Mono persist(K key) { diff --git a/src/main/java/org/springframework/data/redis/core/RedisCommand.java b/src/main/java/org/springframework/data/redis/core/RedisCommand.java index 53dc940a97..1454fd33c2 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisCommand.java +++ b/src/main/java/org/springframework/data/redis/core/RedisCommand.java @@ -77,8 +77,8 @@ public enum RedisCommand { EVALSHA("rw", 2), // EXEC("rw", 0, 0), // EXISTS("r", 1, 1), // - EXPIRE("rw", 2, 2), // - EXPIREAT("rw", 2, 2), // + EXPIRE("rw", 2), // + EXPIREAT("rw", 2), // // -- F FLUSHALL("w", 0, 0), // FLUSHDB("w", 0, 0), // @@ -142,8 +142,8 @@ public enum RedisCommand { MULTI("rw", 0, 0), // // -- P PERSIST("rw", 1, 1), // - PEXPIRE("rw", 2, 2), // - PEXPIREAT("rw", 2, 2), // + PEXPIRE("rw", 2), // + PEXPIREAT("rw", 2), // PING("r", 0, 0), // PSETEX("w", 3), // PSUBSCRIBE("r", 1), // diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java index 8c1ad67ad6..bab7675cc8 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java @@ -25,10 +25,12 @@ import java.util.concurrent.TimeUnit; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.stream.ObjectRecord; import org.springframework.data.redis.core.query.SortQuery; import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.RedisClientInfo; import org.springframework.data.redis.hash.HashMapper; import org.springframework.data.redis.serializer.RedisSerializer; @@ -366,6 +368,24 @@ default Boolean expireAt(K key, Instant expireAt) { return expireAt(key, Date.from(expireAt)); } + /** + * Set the expiration for given {@code key}. + * + * @param key must not be {@literal null}. + * @param expiration must not be {@literal null}. + * @param options must not be {@literal null}. + * @return changes to the expiry. {@literal null} when used in pipeline / transaction. + * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @see Redis Documentation: EXPIRE + * @see Redis Documentation: PEXPIRE + * @see Redis Documentation: EXPIREAT + * @see Redis Documentation: PEXPIREAT + * @see Redis Documentation: PERSIST + * @since 3.5 + */ + @Nullable + ExpireChanges.ExpiryChangeState expire(K key, Expiration expiration, ExpirationOptions options); + /** * Remove the expiration from given {@code key}. * diff --git a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java index 2879f161a4..c80f555ee0 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java @@ -33,6 +33,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.RedisSystemException; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisKeyCommands; @@ -46,6 +47,7 @@ import org.springframework.data.redis.core.script.DefaultScriptExecutor; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.data.redis.core.script.ScriptExecutor; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.RedisClientInfo; import org.springframework.data.redis.hash.HashMapper; import org.springframework.data.redis.hash.ObjectHashMapper; @@ -711,6 +713,16 @@ public Boolean expireAt(K key, final Date date) { }); } + @Nullable + @Override + public ExpireChanges.ExpiryChangeState expire(K key, Expiration expiration, ExpirationOptions options) { + + byte[] rawKey = rawKey(key); + Boolean raw = doWithKeys(connection -> connection.applyExpiration(rawKey, expiration, options)); + + return raw != null ? ExpireChanges.ExpiryChangeState.of(raw) : null; + } + @Override public Boolean persist(K key) { diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java index 18c3c24e20..3233ec59ac 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisMap.java @@ -324,13 +324,13 @@ public Cursor> scan() { } @Override - public BoundHashFieldExpirationOperations expiration() { - return hashOps.expiration(); + public BoundHashFieldExpirationOperations hashFieldExpiration() { + return hashOps.hashExpiration(); } @Override - public BoundHashFieldExpirationOperations expiration(Collection hashFields) { - return hashOps.expiration(hashFields); + public BoundHashFieldExpirationOperations hashFieldExpiration(Collection hashFields) { + return hashOps.hashExpiration(hashFields); } private void checkResult(@Nullable Object obj) { diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java index 955942eb25..d9c8c0a35e 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisMap.java @@ -78,34 +78,35 @@ public interface RedisMap extends RedisStore, ConcurrentMap { Iterator> scan(); /** - * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at - * {@code key}. Operations on the expiration object obtain keys at the time of invoking any expiration operation. + * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at the + * bound {@link #getKey()}. Operations on the expiration object obtain keys at the time of invoking any expiration + * operation. * * @return the bound operations object to perform operations on the hash field expiration. * @since 3.5 */ - BoundHashFieldExpirationOperations expiration(); + BoundHashFieldExpirationOperations hashFieldExpiration(); /** * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at the - * bound {@code key} for the given hash fields. + * bound {@link #getKey()} for the given hash fields. * * @param hashFields collection of hash fields to operate on. * @return the bound operations object to perform operations on the hash field expiration. * @since 3.5 */ - default BoundHashFieldExpirationOperations expiration(K... hashFields) { - return expiration(Arrays.asList(hashFields)); + default BoundHashFieldExpirationOperations hashFieldExpiration(K... hashFields) { + return hashFieldExpiration(Arrays.asList(hashFields)); } /** * Returns a bound operations object to perform operations on the hash field expiration for all hash fields at the - * bound {@code key} for the given hash fields. + * bound {@link #getKey()} for the given hash fields. * * @param hashFields collection of hash fields to operate on. * @return the bound operations object to perform operations on the hash field expiration. * @since 3.5 */ - BoundHashFieldExpirationOperations expiration(Collection hashFields); + BoundHashFieldExpirationOperations hashFieldExpiration(Collection hashFields); } diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java index c6a5f0a45d..5d3534fb6e 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisProperties.java @@ -307,13 +307,13 @@ public Iterator> scan() { } @Override - public BoundHashFieldExpirationOperations expiration() { - return (BoundHashFieldExpirationOperations) delegate.expiration(); + public BoundHashFieldExpirationOperations hashFieldExpiration() { + return (BoundHashFieldExpirationOperations) delegate.hashFieldExpiration(); } @Override - public BoundHashFieldExpirationOperations expiration(Collection hashFields) { - return (BoundHashFieldExpirationOperations) delegate.expiration((Collection) hashFields); + public BoundHashFieldExpirationOperations hashFieldExpiration(Collection hashFields) { + return (BoundHashFieldExpirationOperations) delegate.hashFieldExpiration((Collection) hashFields); } } diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index 573f105247..288aa202b0 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -15,48 +15,24 @@ */ package org.springframework.data.redis.connection; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.fail; -import static org.assertj.core.api.Assertions.within; -import static org.assertj.core.api.Assumptions.assumeThat; -import static org.awaitility.Awaitility.await; -import static org.junit.jupiter.api.condition.OS.MAC; -import static org.springframework.data.redis.connection.BitFieldSubCommands.create; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.FAIL; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.INT_8; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.signed; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.unsigned; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_1; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_2; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_3; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_1; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_2; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_3; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_4; -import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.KILOMETERS; -import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs; -import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.newGeoSearchArgs; -import static org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs.newGeoSearchStoreArgs; -import static org.springframework.data.redis.core.ScanOptions.scanOptions; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; +import static org.awaitility.Awaitility.*; +import static org.junit.jupiter.api.condition.OS.*; +import static org.springframework.data.redis.connection.BitFieldSubCommands.*; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.*; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.*; +import static org.springframework.data.redis.connection.ClusterTestVariables.*; +import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.*; +import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.*; +import static org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs.*; +import static org.springframework.data.redis.core.ScanOptions.*; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; @@ -71,6 +47,7 @@ import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.domain.Range; @@ -215,6 +192,23 @@ void testExpire() { await().atMost(Duration.ofMillis(3000L)).until(keyExpired::passes); } + @LongRunningTest // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + void testExpireWithArgs() { + + actual.add(connection.set("exp", "true")); + actual.add( + connection.applyExpiration("exp".getBytes(), Expiration.from(Duration.ofMinutes(1)), ExpirationOptions.none())); + actual.add(connection.applyExpiration("exp".getBytes(), Expiration.from(Duration.ofMinutes(1)), + ExpirationOptions.builder().nx().build())); + actual.add(connection.applyExpiration("exp".getBytes(), Expiration.from(Duration.ofMinutes(2)), + ExpirationOptions.builder().gt().build())); + actual.add(connection.applyExpiration("exp".getBytes(), Expiration.from(Duration.ofMinutes(3)), + ExpirationOptions.builder().lt().build())); + + verifyResults(Arrays.asList(true, true, false, true, false)); + } + @Test // DATAREDIS-1103 void testSetWithKeepTTL() { @@ -777,7 +771,25 @@ void testExecute() { assertThat(stringSerializer.deserialize((byte[]) getResults().get(1))).isEqualTo("bar"); } - @Test // GH- + @Test // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + void testExecuteExpirationWithConditions() { + + actual.add(connection.set("foo", "bar")); + actual.add(connection.execute("TTL", "foo")); + actual.add(connection.execute("EXPIRE", "foo", "100", "NX")); + actual.add(connection.execute("PERSIST", "foo")); + actual.add(connection.execute("TTL", "foo")); + + List results = getResults(); + + assertThat(results.get(1)).isEqualTo(-1L); + assertThat(results.get(2)).isIn(1L, true); + assertThat(results.get(3)).isIn(1L, true); + assertThat(results.get(4)).isEqualTo(-1L); + } + + @Test // GH-3054 @EnabledOnCommand("HEXPIRE") void testExecuteHashFieldExpiration() { diff --git a/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java index ad64444cc1..193ac17190 100644 --- a/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java @@ -119,7 +119,7 @@ public interface ClusterConnectionTests { void expireAtShouldBeSetCorrectly(); // DATAREDIS-315 - void expireShouldBeSetCorreclty(); + void expireShouldBeSetCorrectly(); // DATAREDIS-315 void flushDbOnSingleNodeShouldFlushOnlyGivenNodesDb(); @@ -386,7 +386,7 @@ public interface ClusterConnectionTests { void pExpireAtShouldBeSetCorrectly(); // DATAREDIS-315 - void pExpireShouldBeSetCorreclty(); + void pExpireShouldBeSetCorrectly(); // DATAREDIS-315 void pSetExShouldSetValueCorrectly(); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java index 4efcf03168..5e9c1b38e6 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java @@ -273,28 +273,28 @@ public void testExists() { @Test public void testExpireBytes() { - doReturn(true).when(nativeConnection).expire(fooBytes, 1L); + doReturn(true).when(nativeConnection).expire(fooBytes, 1L, ExpirationOptions.Condition.ALWAYS); actual.add(connection.expire(fooBytes, 1L)); verifyResults(Collections.singletonList(true)); } @Test public void testExpire() { - doReturn(true).when(nativeConnection).expire(fooBytes, 1L); + doReturn(true).when(nativeConnection).expire(fooBytes, 1L, ExpirationOptions.Condition.ALWAYS); actual.add(connection.expire(foo, 1L)); verifyResults(Collections.singletonList(true)); } @Test public void testExpireAtBytes() { - doReturn(true).when(nativeConnection).expireAt(fooBytes, 1L); + doReturn(true).when(nativeConnection).expireAt(fooBytes, 1L, ExpirationOptions.Condition.ALWAYS); actual.add(connection.expireAt(fooBytes, 1L)); verifyResults(Collections.singletonList(true)); } @Test public void testExpireAt() { - doReturn(true).when(nativeConnection).expireAt(fooBytes, 1L); + doReturn(true).when(nativeConnection).expireAt(fooBytes, 1L, ExpirationOptions.Condition.ALWAYS); actual.add(connection.expireAt(foo, 1L)); verifyResults(Collections.singletonList(true)); } @@ -1662,28 +1662,28 @@ public void testZUnionStore() { @Test public void testPExpireBytes() { - doReturn(true).when(nativeConnection).pExpire(fooBytes, 34L); + doReturn(true).when(nativeConnection).pExpire(fooBytes, 34L, ExpirationOptions.Condition.ALWAYS); actual.add(connection.pExpire(fooBytes, 34L)); verifyResults(Collections.singletonList(true)); } @Test public void testPExpire() { - doReturn(true).when(nativeConnection).pExpire(fooBytes, 34L); + doReturn(true).when(nativeConnection).pExpire(fooBytes, 34L, ExpirationOptions.Condition.ALWAYS); actual.add(connection.pExpire(foo, 34L)); verifyResults(Collections.singletonList(true)); } @Test public void testPExpireAtBytes() { - doReturn(true).when(nativeConnection).pExpireAt(fooBytes, 34L); + doReturn(true).when(nativeConnection).pExpireAt(fooBytes, 34L, ExpirationOptions.Condition.ALWAYS); actual.add(connection.pExpireAt(fooBytes, 34L)); verifyResults(Collections.singletonList(true)); } @Test public void testPExpireAt() { - doReturn(true).when(nativeConnection).pExpireAt(fooBytes, 34L); + doReturn(true).when(nativeConnection).pExpireAt(fooBytes, 34L, ExpirationOptions.Condition.ALWAYS); actual.add(connection.pExpireAt(foo, 34L)); verifyResults(Collections.singletonList(true)); } diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 4bed88c0a5..b4229d9522 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -15,37 +15,18 @@ */ package org.springframework.data.redis.connection.jedis; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.fail; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.data.Offset.*; import static org.assertj.core.data.Offset.offset; -import static org.springframework.data.redis.connection.BitFieldSubCommands.create; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.FAIL; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.INT_8; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.signed; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.unsigned; -import static org.springframework.data.redis.connection.ClusterTestVariables.CLUSTER_HOST; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_1; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_2; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_3; -import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_1_PORT; -import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_2_PORT; -import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_3_PORT; -import static org.springframework.data.redis.connection.ClusterTestVariables.REPLICAOF_NODE_1_PORT; -import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_1; -import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_2; -import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_3; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_1; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_2; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_3; -import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.KILOMETERS; -import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs; -import static org.springframework.data.redis.connection.RedisListCommands.Direction; -import static org.springframework.data.redis.connection.RedisListCommands.Position; -import static org.springframework.data.redis.connection.RedisZSetCommands.Range; -import static org.springframework.data.redis.core.ScanOptions.NONE; -import static org.springframework.data.redis.core.ScanOptions.scanOptions; +import static org.springframework.data.redis.connection.BitFieldSubCommands.*; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.*; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.*; +import static org.springframework.data.redis.connection.ClusterTestVariables.*; +import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.*; +import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.*; +import static org.springframework.data.redis.connection.RedisListCommands.*; +import static org.springframework.data.redis.connection.RedisZSetCommands.*; +import static org.springframework.data.redis.core.ScanOptions.*; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.HostAndPort; @@ -57,23 +38,14 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; +import java.util.*; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.ExtendWith; + import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Range.Bound; @@ -81,21 +53,13 @@ import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.Point; -import org.springframework.data.redis.connection.BitFieldSubCommands; -import org.springframework.data.redis.connection.ClusterConnectionTests; -import org.springframework.data.redis.connection.ClusterSlotHashUtil; -import org.springframework.data.redis.connection.ClusterTopology; -import org.springframework.data.redis.connection.DataType; -import org.springframework.data.redis.connection.DefaultSortParameters; +import org.springframework.data.redis.connection.*; import org.springframework.data.redis.connection.Limit; -import org.springframework.data.redis.connection.RedisClusterNode; import org.springframework.data.redis.connection.RedisClusterNode.SlotRange; import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation; -import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisServerCommands.FlushOption; import org.springframework.data.redis.connection.RedisStringCommands.BitOperation; import org.springframework.data.redis.connection.RedisStringCommands.SetOption; -import org.springframework.data.redis.connection.ReturnType; import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding; import org.springframework.data.redis.connection.zset.DefaultTuple; import org.springframework.data.redis.connection.zset.Tuple; @@ -472,8 +436,24 @@ public void expireAtShouldBeSetCorrectly() { assertThat(nativeConnection.ttl(JedisConverters.toString(KEY_1_BYTES))).isGreaterThan(1); } + @Test // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + public void expireAtWithConditionShouldBeSetCorrectly() { + + nativeConnection.set(KEY_1, VALUE_1); + + assertThat(clusterConnection.expireAt(KEY_1_BYTES, System.currentTimeMillis() / 1000 + 5000, + ExpirationOptions.Condition.XX)).isFalse(); + assertThat(clusterConnection.expireAt(KEY_1_BYTES, System.currentTimeMillis() / 1000 + 5000, + ExpirationOptions.Condition.NX)).isTrue(); + assertThat(clusterConnection.expireAt(KEY_1_BYTES, System.currentTimeMillis() / 1000 + 15000, + ExpirationOptions.Condition.LT)).isFalse(); + + assertThat(nativeConnection.ttl(JedisConverters.toString(KEY_1_BYTES))).isGreaterThan(1); + } + @Test // DATAREDIS-315 - public void expireShouldBeSetCorreclty() { + public void expireShouldBeSetCorrectly() { nativeConnection.set(KEY_1, VALUE_1); @@ -482,6 +462,19 @@ public void expireShouldBeSetCorreclty() { assertThat(nativeConnection.ttl(JedisConverters.toString(KEY_1_BYTES))).isGreaterThan(1); } + @Test // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + public void expireWithConditionShouldBeSetCorrectly() { + + nativeConnection.set(KEY_1, VALUE_1); + + assertThat(clusterConnection.expire(KEY_1_BYTES, 15, ExpirationOptions.Condition.XX)).isFalse(); + assertThat(clusterConnection.expire(KEY_1_BYTES, 15, ExpirationOptions.Condition.NX)).isTrue(); + assertThat(clusterConnection.expire(KEY_1_BYTES, 15, ExpirationOptions.Condition.LT)).isFalse(); + + assertThat(nativeConnection.ttl(JedisConverters.toString(KEY_1_BYTES))).isGreaterThan(1); + } + @Test // DATAREDIS-315 public void flushDbOnSingleNodeShouldFlushOnlyGivenNodesDb() { @@ -1597,8 +1590,27 @@ public void pExpireAtShouldBeSetCorrectly() { assertThat(nativeConnection.ttl(JedisConverters.toString(KEY_1_BYTES))).isGreaterThan(1); } + @Test // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + public void pExpireAtWithConditionShouldBeSetCorrectly() { + + nativeConnection.set(KEY_1, VALUE_1); + + assertThat( + clusterConnection.pExpireAt(KEY_1_BYTES, System.currentTimeMillis() + 5000, ExpirationOptions.Condition.XX)) + .isFalse(); + assertThat( + clusterConnection.pExpireAt(KEY_1_BYTES, System.currentTimeMillis() + 5000, ExpirationOptions.Condition.NX)) + .isTrue(); + assertThat( + clusterConnection.pExpireAt(KEY_1_BYTES, System.currentTimeMillis() + 15000, ExpirationOptions.Condition.LT)) + .isFalse(); + + assertThat(nativeConnection.ttl(JedisConverters.toString(KEY_1_BYTES))).isGreaterThan(1); + } + @Test // DATAREDIS-315 - public void pExpireShouldBeSetCorreclty() { + public void pExpireShouldBeSetCorrectly() { nativeConnection.set(KEY_1, VALUE_1); @@ -1607,6 +1619,19 @@ public void pExpireShouldBeSetCorreclty() { assertThat(nativeConnection.ttl(JedisConverters.toString(KEY_1_BYTES))).isGreaterThan(1); } + @Test // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + public void pExpireWithConditionShouldBeSetCorrectly() { + + nativeConnection.set(KEY_1, VALUE_1); + + assertThat(clusterConnection.pExpire(KEY_1_BYTES, 15000, ExpirationOptions.Condition.XX)).isFalse(); + assertThat(clusterConnection.pExpire(KEY_1_BYTES, 15000, ExpirationOptions.Condition.NX)).isTrue(); + assertThat(clusterConnection.pExpire(KEY_1_BYTES, 15000, ExpirationOptions.Condition.LT)).isFalse(); + + assertThat(nativeConnection.ttl(JedisConverters.toString(KEY_1_BYTES))).isGreaterThan(1); + } + @Test // DATAREDIS-315 public void pSetExShouldSetValueCorrectly() { diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java index 1d45dc739e..5590b18275 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java @@ -15,35 +15,17 @@ */ package org.springframework.data.redis.connection.lettuce; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.data.Offset.*; import static org.assertj.core.data.Offset.offset; -import static org.springframework.data.redis.connection.BitFieldSubCommands.create; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.FAIL; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.INT_8; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.signed; -import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.unsigned; -import static org.springframework.data.redis.connection.ClusterTestVariables.CLUSTER_HOST; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_1; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_2; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_3; -import static org.springframework.data.redis.connection.ClusterTestVariables.KEY_4; -import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_1_PORT; -import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_2_PORT; -import static org.springframework.data.redis.connection.ClusterTestVariables.MASTER_NODE_3_PORT; -import static org.springframework.data.redis.connection.ClusterTestVariables.REPLICAOF_NODE_1_PORT; -import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_1; -import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_2; -import static org.springframework.data.redis.connection.ClusterTestVariables.SAME_SLOT_KEY_3; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_1; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_2; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_3; -import static org.springframework.data.redis.connection.ClusterTestVariables.VALUE_4; -import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.KILOMETERS; -import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs; -import static org.springframework.data.redis.connection.RedisZSetCommands.Range; -import static org.springframework.data.redis.core.ScanOptions.scanOptions; +import static org.springframework.data.redis.connection.BitFieldSubCommands.*; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.*; +import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.*; +import static org.springframework.data.redis.connection.ClusterTestVariables.*; +import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.*; +import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.*; +import static org.springframework.data.redis.connection.RedisZSetCommands.*; +import static org.springframework.data.redis.core.ScanOptions.*; import io.lettuce.core.cluster.RedisClusterClient; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; @@ -52,17 +34,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; +import java.util.*; import java.util.concurrent.TimeUnit; import org.assertj.core.data.Offset; @@ -71,6 +43,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.ExtendWith; + import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Range.Bound; @@ -78,21 +51,11 @@ import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.Point; -import org.springframework.data.redis.connection.BitFieldSubCommands; -import org.springframework.data.redis.connection.ClusterConnectionTests; -import org.springframework.data.redis.connection.ClusterSlotHashUtil; -import org.springframework.data.redis.connection.ClusterTestVariables; -import org.springframework.data.redis.connection.DataType; -import org.springframework.data.redis.connection.DefaultSortParameters; +import org.springframework.data.redis.connection.*; import org.springframework.data.redis.connection.Limit; -import org.springframework.data.redis.connection.RedisClusterConfiguration; -import org.springframework.data.redis.connection.RedisClusterConnection; -import org.springframework.data.redis.connection.RedisClusterNode; import org.springframework.data.redis.connection.RedisClusterNode.SlotRange; import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation; -import org.springframework.data.redis.connection.RedisListCommands; import org.springframework.data.redis.connection.RedisListCommands.Position; -import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisServerCommands.FlushOption; import org.springframework.data.redis.connection.RedisStringCommands.BitOperation; import org.springframework.data.redis.connection.RedisStringCommands.SetOption; @@ -538,8 +501,24 @@ public void expireAtShouldBeSetCorrectly() { assertThat(nativeConnection.ttl(LettuceConverters.toString(KEY_1_BYTES))).isGreaterThan(1); } + @Test // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + public void expireAtWithConditionShouldBeSetCorrectly() { + + nativeConnection.set(KEY_1, VALUE_1); + + assertThat(clusterConnection.expireAt(KEY_1_BYTES, System.currentTimeMillis() / 1000 + 5000, + ExpirationOptions.Condition.XX)).isFalse(); + assertThat(clusterConnection.expireAt(KEY_1_BYTES, System.currentTimeMillis() / 1000 + 5000, + ExpirationOptions.Condition.NX)).isTrue(); + assertThat(clusterConnection.expireAt(KEY_1_BYTES, System.currentTimeMillis() / 1000 + 10000, + ExpirationOptions.Condition.LT)).isFalse(); + + assertThat(nativeConnection.ttl(LettuceConverters.toString(KEY_1_BYTES))).isGreaterThan(1); + } + @Test // DATAREDIS-315 - public void expireShouldBeSetCorreclty() { + public void expireShouldBeSetCorrectly() { nativeConnection.set(KEY_1, VALUE_1); @@ -548,6 +527,19 @@ public void expireShouldBeSetCorreclty() { assertThat(nativeConnection.ttl(LettuceConverters.toString(KEY_1_BYTES))).isGreaterThan(1); } + @Test // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + public void expireWithConditionShouldBeSetCorrectly() { + + nativeConnection.set(KEY_1, VALUE_1); + + assertThat(clusterConnection.expire(KEY_1_BYTES, 15, ExpirationOptions.Condition.XX)).isFalse(); + assertThat(clusterConnection.expire(KEY_1_BYTES, 15, ExpirationOptions.Condition.NX)).isTrue(); + assertThat(clusterConnection.expire(KEY_1_BYTES, 15, ExpirationOptions.Condition.LT)).isFalse(); + + assertThat(nativeConnection.ttl(LettuceConverters.toString(KEY_1_BYTES))).isGreaterThan(1); + } + @Test // DATAREDIS-315 public void flushDbOnSingleNodeShouldFlushOnlyGivenNodesDb() { @@ -1666,8 +1658,27 @@ public void pExpireAtShouldBeSetCorrectly() { assertThat(nativeConnection.ttl(LettuceConverters.toString(KEY_1_BYTES))).isGreaterThan(1); } + @Test // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + public void pExpireAtWithConditionShouldBeSetCorrectly() { + + nativeConnection.set(KEY_1, VALUE_1); + + assertThat( + clusterConnection.pExpireAt(KEY_1_BYTES, System.currentTimeMillis() + 5000, ExpirationOptions.Condition.XX)) + .isFalse(); + assertThat( + clusterConnection.pExpireAt(KEY_1_BYTES, System.currentTimeMillis() + 5000, ExpirationOptions.Condition.NX)) + .isTrue(); + assertThat( + clusterConnection.pExpireAt(KEY_1_BYTES, System.currentTimeMillis() + 15000, ExpirationOptions.Condition.LT)) + .isFalse(); + + assertThat(nativeConnection.ttl(LettuceConverters.toString(KEY_1_BYTES))).isGreaterThan(1); + } + @Test // DATAREDIS-315 - public void pExpireShouldBeSetCorreclty() { + public void pExpireShouldBeSetCorrectly() { nativeConnection.set(KEY_1, VALUE_1); @@ -1676,6 +1687,19 @@ public void pExpireShouldBeSetCorreclty() { assertThat(nativeConnection.ttl(LettuceConverters.toString(KEY_1_BYTES))).isGreaterThan(1); } + @Test // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + public void pExpireWithConditionShouldBeSetCorrectly() { + + nativeConnection.set(KEY_1, VALUE_1); + + assertThat(clusterConnection.pExpire(KEY_1_BYTES, 15000, ExpirationOptions.Condition.XX)).isFalse(); + assertThat(clusterConnection.pExpire(KEY_1_BYTES, 15000, ExpirationOptions.Condition.NX)).isTrue(); + assertThat(clusterConnection.pExpire(KEY_1_BYTES, 15000, ExpirationOptions.Condition.LT)).isFalse(); + + assertThat(nativeConnection.ttl(LettuceConverters.toString(KEY_1_BYTES))).isGreaterThan(1); + } + @Test // DATAREDIS-315 public void pSetExShouldSetValueCorrectly() { diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java index 3376c2727f..bc16ae8a46 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java @@ -32,11 +32,15 @@ import org.springframework.data.redis.RedisSystemException; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; +import org.springframework.data.redis.connection.ReactiveKeyCommands; +import org.springframework.data.redis.connection.ReactiveRedisConnection; import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand; import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding; import org.springframework.data.redis.core.KeyScanOptions; import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.condition.EnabledOnRedisVersion; import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest; @@ -303,6 +307,35 @@ void shouldExpireKeysCorrectly() { assertThat(nativeCommands.ttl(KEY_1)).isGreaterThan(8L); } + @ParameterizedRedisTest // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + void shouldExpireWithOptionsKeysCorrectly() { + + nativeCommands.set(KEY_1, VALUE_1); + + connection.keyCommands() + .applyExpiration( + Mono.just(ReactiveKeyCommands.ExpireCommand.expire(KEY_1_BBUFFER, Expiration.from(Duration.ofSeconds(10))) + .withOptions(ExpirationOptions.builder().xx().build()))) + .map(ReactiveRedisConnection.BooleanResponse::getOutput).as(StepVerifier::create) // + .expectNext(false) // + .expectComplete() // + .verify(); + + assertThat(nativeCommands.ttl(KEY_1)).isEqualTo(-1L); + + connection.keyCommands() + .applyExpiration( + Mono.just(ReactiveKeyCommands.ExpireCommand.expire(KEY_1_BBUFFER, Expiration.from(Duration.ofSeconds(10))) + .withOptions(ExpirationOptions.builder().nx().build()))) + .map(ReactiveRedisConnection.BooleanResponse::getOutput).as(StepVerifier::create) // + .expectNext(true) // + .expectComplete() // + .verify(); + + assertThat(nativeCommands.ttl(KEY_1)).isGreaterThan(8L); + } + @ParameterizedRedisTest // DATAREDIS-602, DATAREDIS-1031 void shouldPreciseExpireKeysCorrectly() { diff --git a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java index 6499ae325a..4982e72973 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultHashOperationsIntegrationTests.java @@ -15,9 +15,8 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; import java.io.IOException; import java.time.Duration; @@ -30,10 +29,11 @@ import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; + import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.StringObjectFactory; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension; import org.springframework.data.redis.core.ExpireChanges.ExpiryChangeState; @@ -281,7 +281,7 @@ void testBoundExpireAndGetExpireSeconds() { hashOps.put(key, key2, val2); BoundHashOperations hashOps = redisTemplate.boundHashOps(key); - BoundHashFieldExpirationOperations exp = hashOps.expiration(key1, key2); + BoundHashFieldExpirationOperations exp = hashOps.hashExpiration(key1, key2); assertThat(exp.expire(Duration.ofSeconds(5))).satisfies(changes -> { assertThat(changes.allOk()).isTrue(); @@ -350,7 +350,7 @@ void testExpireWithOptionsNone() { hashOps.put(key, key2, val2); ExpireChanges expire = redisTemplate.opsForHash().expire(key, - org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), List.of(key1)); + org.springframework.data.redis.core.types.Expiration.seconds(20), ExpirationOptions.none(), List.of(key1)); assertThat(expire.allOk()).isTrue(); } @@ -369,12 +369,12 @@ void testExpireWithOptions() { hashOps.put(key, key2, val2); redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), - FieldExpirationOptions.none(), List.of(key1)); + ExpirationOptions.none(), List.of(key1)); redisTemplate.opsForHash().expire(key, org.springframework.data.redis.core.types.Expiration.seconds(60), - FieldExpirationOptions.none(), List.of(key2)); + ExpirationOptions.none(), List.of(key2)); ExpireChanges changes = redisTemplate.opsForHash().expire(key, - org.springframework.data.redis.core.types.Expiration.seconds(30), FieldExpirationOptions.builder().gt().build(), + org.springframework.data.redis.core.types.Expiration.seconds(30), ExpirationOptions.builder().gt().build(), List.of(key1, key2)); assertThat(changes.ok()).containsExactly(key1); diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java index 48532b2feb..a387f2ae3c 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveHashOperationsIntegrationTests.java @@ -15,9 +15,9 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; -import static org.junit.jupiter.api.condition.OS.MAC; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; +import static org.junit.jupiter.api.condition.OS.*; import reactor.test.StepVerifier; @@ -33,11 +33,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.condition.DisabledOnOs; + import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.RawObjectFactory; import org.springframework.data.redis.SettingsUtils; import org.springframework.data.redis.StringObjectFactory; -import org.springframework.data.redis.connection.Hash.FieldExpirationOptions; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.convert.Converters; @@ -543,14 +544,14 @@ void testExpireWithOptions() { putAll(key, key1, val1, key2, val2); hashOperations - .expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), FieldExpirationOptions.none(), + .expire(key, org.springframework.data.redis.core.types.Expiration.seconds(20), ExpirationOptions.none(), List.of(key1)) .as(StepVerifier::create)// .assertNext(changes -> { assertThat(changes.allOk()).isTrue(); }).verifyComplete(); hashOperations - .expire(key, org.springframework.data.redis.core.types.Expiration.seconds(60), FieldExpirationOptions.none(), + .expire(key, org.springframework.data.redis.core.types.Expiration.seconds(60), ExpirationOptions.none(), List.of(key2)) .as(StepVerifier::create)// .assertNext(changes -> { @@ -559,7 +560,7 @@ void testExpireWithOptions() { hashOperations .expire(key, org.springframework.data.redis.core.types.Expiration.seconds(30), - FieldExpirationOptions.builder().gt().build(), List.of(key1, key2)) + ExpirationOptions.builder().gt().build(), List.of(key1, key2)) .as(StepVerifier::create)// .assertNext(changes -> { assertThat(changes.ok()).containsExactly(key1); diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java index bd8eb0b141..f3c55deb55 100644 --- a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java @@ -37,6 +37,7 @@ import org.springframework.data.redis.PersonObjectFactory; import org.springframework.data.redis.StringObjectFactory; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.ReactiveRedisClusterConnection; import org.springframework.data.redis.connection.ReactiveSubscription.ChannelMessage; import org.springframework.data.redis.connection.ReactiveSubscription.Message; @@ -45,6 +46,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.ReactiveOperationsTestParams.Fixture; import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisElementReader; @@ -323,6 +325,24 @@ void expire() { .consumeNextWith(actual -> assertThat(actual).isGreaterThan(Duration.ofSeconds(8))).verifyComplete(); } + @ParameterizedRedisTest // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + void expireWithCondition() { + + K key = keyFactory.instance(); + V value = valueFactory.instance(); + + redisTemplate.opsForValue().set(key, value).as(StepVerifier::create).expectNext(true).verifyComplete(); + + redisTemplate.expire(key, Expiration.seconds(10), ExpirationOptions.none()).as(StepVerifier::create) + .expectNext(ExpireChanges.ExpiryChangeState.OK).verifyComplete(); + redisTemplate.expire(key, Expiration.seconds(20), ExpirationOptions.builder().lt().build()).as(StepVerifier::create) + .expectNext(ExpireChanges.ExpiryChangeState.CONDITION_NOT_MET).verifyComplete(); + + redisTemplate.getExpire(key).as(StepVerifier::create) // + .consumeNextWith(actual -> assertThat(actual).isGreaterThan(Duration.ofSeconds(5))).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-602 void preciseExpire() { @@ -337,6 +357,24 @@ void preciseExpire() { .consumeNextWith(actual -> assertThat(actual).isGreaterThan(Duration.ofSeconds(8))).verifyComplete(); } + @ParameterizedRedisTest // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + void preciseExpireWithCondition() { + + K key = keyFactory.instance(); + V value = valueFactory.instance(); + + redisTemplate.opsForValue().set(key, value).as(StepVerifier::create).expectNext(true).verifyComplete(); + + redisTemplate.expire(key, Expiration.milliseconds(10000), ExpirationOptions.none()).as(StepVerifier::create) + .expectNext(ExpireChanges.ExpiryChangeState.OK).verifyComplete(); + redisTemplate.expire(key, Expiration.milliseconds(20000), ExpirationOptions.builder().lt().build()) + .as(StepVerifier::create).expectNext(ExpireChanges.ExpiryChangeState.CONDITION_NOT_MET).verifyComplete(); + + redisTemplate.getExpire(key).as(StepVerifier::create) // + .consumeNextWith(actual -> assertThat(actual).isGreaterThan(Duration.ofSeconds(5))).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-602 void expireAt() { diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java index 31e93d06ac..f587339188 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java @@ -15,9 +15,8 @@ */ package org.springframework.data.redis.support.collections; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assumptions.assumeThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.*; +import static org.assertj.core.api.Assumptions.*; import java.io.IOException; import java.text.DecimalFormat; @@ -36,6 +35,7 @@ import org.assertj.core.api.Assumptions; import org.junit.jupiter.api.BeforeEach; + import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.DoubleAsStringObjectFactory; import org.springframework.data.redis.LongAsStringObjectFactory; @@ -205,7 +205,7 @@ void testExpire() { V v1 = getValue(); assertThat(map.put(k1, v1)).isEqualTo(null); - BoundHashFieldExpirationOperations ops = map.expiration(Collections.singletonList(k1)); + BoundHashFieldExpirationOperations ops = map.hashFieldExpiration(Collections.singletonList(k1)); assertThat(ops.expire(Duration.ofSeconds(5))).satisfies(ExpireChanges::allOk); assertThat(ops.getTimeToLive()).satisfies(expiration -> { assertThat(expiration.expirationOf(k1).raw()).isBetween(1L, 5L); @@ -224,7 +224,7 @@ void testExpireAt() { V v1 = getValue(); assertThat(map.put(k1, v1)).isEqualTo(null); - BoundHashFieldExpirationOperations ops = map.expiration(Collections.singletonList(k1)); + BoundHashFieldExpirationOperations ops = map.hashFieldExpiration(Collections.singletonList(k1)); assertThat(ops.expireAt(Instant.now().plusSeconds(5))).satisfies(ExpireChanges::allOk); assertThat(ops.getTimeToLive()).satisfies(expiration -> { assertThat(expiration.expirationOf(k1).raw()).isBetween(1L, 5L); From e4009f47c4781e18e79a78ed35b3f510ab6793d2 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 5 Mar 2025 16:51:40 +0100 Subject: [PATCH 143/187] Introduce `BoundKeyExpirationOperations`. And rename entry points for Hash Field expiration from expiration to hashFieldExpiration. Original Pull Request: #3115 --- .../core/BoundKeyExpirationOperations.java | 111 ++++++++++++++++++ .../data/redis/core/BoundKeyOperations.java | 16 +++ .../core/BoundOperationsProxyFactory.java | 7 +- .../DefaultBoundKeyExpirationOperations.java | 99 ++++++++++++++++ .../data/redis/core/RedisOperations.java | 10 ++ .../support/atomic/RedisAtomicDouble.java | 6 + .../support/atomic/RedisAtomicInteger.java | 5 + .../redis/support/atomic/RedisAtomicLong.java | 6 + .../core/RedisTemplateIntegrationTests.java | 43 +++++++ 9 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/springframework/data/redis/core/BoundKeyExpirationOperations.java create mode 100644 src/main/java/org/springframework/data/redis/core/DefaultBoundKeyExpirationOperations.java diff --git a/src/main/java/org/springframework/data/redis/core/BoundKeyExpirationOperations.java b/src/main/java/org/springframework/data/redis/core/BoundKeyExpirationOperations.java new file mode 100644 index 0000000000..b50431b209 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/core/BoundKeyExpirationOperations.java @@ -0,0 +1,111 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.core; + +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.connection.ExpirationOptions; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.core.types.Expirations; +import org.springframework.lang.Nullable; + +/** + * Key Expiration operations bound to a key. + * + * @author Mark Paluch + * @since 3.5 + */ +public interface BoundKeyExpirationOperations { + + /** + * Apply {@link Expiration} to the bound key without any additional constraints. + * + * @param expiration the expiration definition. + * @return changes to the key. {@literal null} when used in pipeline / transaction. + */ + default ExpireChanges.ExpiryChangeState expire(Expiration expiration) { + return expire(expiration, ExpirationOptions.none()); + } + + /** + * Apply {@link Expiration} to the bound key given {@link ExpirationOptions expiration options}. + * + * @param expiration the expiration definition. + * @param options expiration options. + * @return changes to the key. {@literal null} when used in pipeline / transaction. + */ + @Nullable + ExpireChanges.ExpiryChangeState expire(Expiration expiration, ExpirationOptions options); + + /** + * Set time to live for the bound key. + * + * @param timeout the amount of time after which the key will be expired, must not be {@literal null}. + * @return changes to the key. {@literal null} when used in pipeline / transaction. + * @throws IllegalArgumentException if the timeout is {@literal null}. + * @see Redis Documentation: EXPIRE + * @since 3.5 + */ + @Nullable + ExpireChanges.ExpiryChangeState expire(Duration timeout); + + /** + * Set the expiration for the bound key as a {@literal date} timestamp. + * + * @param expireAt must not be {@literal null}. + * @return changes to the key. {@literal null} when used in pipeline / transaction. + * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @see Redis Documentation: EXPIRE + * @since 3.5 + */ + @Nullable + ExpireChanges.ExpiryChangeState expireAt(Instant expireAt); + + /** + * Remove the expiration from the bound key. + * + * @return changes to the key. {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: PERSIST + * @since 3.5 + */ + @Nullable + ExpireChanges.ExpiryChangeState persist(); + + /** + * Get the time to live for the bound key in seconds. + * + * @return the actual expirations in seconds for the key. {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: TTL + * @since 3.5 + */ + @Nullable + Expirations.TimeToLive getTimeToLive(); + + /** + * Get the time to live for the bound key and convert it to the given {@link TimeUnit}. + * + * @param timeUnit must not be {@literal null}. + * @return the actual expirations for the key in the given time unit. {@literal null} when used in pipeline / + * transaction. + * @see Redis Documentation: TTL + * @since 3.5 + */ + @Nullable + Expirations.TimeToLive getTimeToLive(TimeUnit timeUnit); + +} diff --git a/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java b/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java index e9d1f5e57c..bb8b438a75 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundKeyOperations.java @@ -51,6 +51,16 @@ public interface BoundKeyOperations { @Nullable DataType getType(); + /** + * Returns a bound operations object to perform expiration operations on the bound key. + * + * @return the bound operations object to perform operations on the hash field expiration. + * @since 3.5 + */ + default BoundKeyExpirationOperations expiration() { + return new DefaultBoundKeyExpirationOperations<>(getOperations(), getKey()); + } + /** * Returns the expiration of this key. * @@ -127,4 +137,10 @@ default Boolean expireAt(Instant expireAt) { * @param newKey new key. Must not be {@literal null}. */ void rename(K newKey); + + /** + * @return never {@literal null}. + */ + RedisOperations getOperations(); + } diff --git a/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java b/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java index 3492553b28..2c4bfc5f49 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java +++ b/src/main/java/org/springframework/data/redis/core/BoundOperationsProxyFactory.java @@ -143,7 +143,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable { delegate.rename(invocation.getArguments()[0]); yield null; } - case "getOperations" -> delegate.getOps(); + case "getOperations" -> delegate.getOperations(); default -> method.getDeclaringClass() == boundOperationsInterface ? doInvoke(invocation, method, operationsTarget, true) : doInvoke(invocation, method, delegate, false); @@ -234,12 +234,15 @@ public void rename(Object newKey) { key = newKey; } + @Override public DataType getType() { return type; } - public RedisOperations getOps() { + @Override + public RedisOperations getOperations() { return ops; } + } } diff --git a/src/main/java/org/springframework/data/redis/core/DefaultBoundKeyExpirationOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultBoundKeyExpirationOperations.java new file mode 100644 index 0000000000..9ff186cd3e --- /dev/null +++ b/src/main/java/org/springframework/data/redis/core/DefaultBoundKeyExpirationOperations.java @@ -0,0 +1,99 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.core; + +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.connection.ExpirationOptions; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.core.types.Expirations; +import org.springframework.lang.Nullable; + +/** + * Default {@link BoundKeyExpirationOperations} implementation. + * + * @author Mark Paluch + * @since 3.5 + */ +class DefaultBoundKeyExpirationOperations implements BoundKeyExpirationOperations { + + private final RedisOperations operations; + private final K key; + + public DefaultBoundKeyExpirationOperations(RedisOperations operations, K key) { + this.operations = operations; + this.key = key; + } + + @Nullable + @Override + public ExpireChanges.ExpiryChangeState expire(Expiration expiration, ExpirationOptions options) { + return operations.expire(key, expiration, options); + } + + @Nullable + @Override + public ExpireChanges.ExpiryChangeState expire(Duration timeout) { + + Boolean expire = operations.expire(key, timeout); + + return toExpiryChangeState(expire); + } + + @Nullable + @Override + public ExpireChanges.ExpiryChangeState expireAt(Instant expireAt) { + return toExpiryChangeState(operations.expireAt(key, expireAt)); + } + + @Nullable + @Override + public ExpireChanges.ExpiryChangeState persist() { + return toExpiryChangeState(operations.persist(key)); + } + + @Nullable + @Override + public Expirations.TimeToLive getTimeToLive() { + + Long expire = operations.getExpire(key); + + return expire == null ? null : Expirations.TimeToLive.of(expire, TimeUnit.SECONDS); + } + + @Nullable + @Override + public Expirations.TimeToLive getTimeToLive(TimeUnit timeUnit) { + + Long expire = operations.getExpire(key, timeUnit); + + return expire == null ? null : Expirations.TimeToLive.of(expire, timeUnit); + + } + + @Nullable + private static ExpireChanges.ExpiryChangeState toExpiryChangeState(@Nullable Boolean result) { + + if (result == null) { + return null; + } + + return ExpireChanges.ExpiryChangeState.of(result); + } + +} diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java index bab7675cc8..bb11c66a00 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java @@ -386,6 +386,16 @@ default Boolean expireAt(K key, Instant expireAt) { @Nullable ExpireChanges.ExpiryChangeState expire(K key, Expiration expiration, ExpirationOptions options); + /** + * Returns a bound operations object to perform expiration operations on the bound key. + * + * @return the bound operations object to perform operations on the hash field expiration. + * @since 3.5 + */ + default BoundKeyExpirationOperations expiration(K key) { + return new DefaultBoundKeyExpirationOperations<>(this, key); + } + /** * Remove the expiration from given {@code key}. * diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java index a60d55ad3c..a95f78b3e5 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicDouble.java @@ -399,6 +399,11 @@ public void rename(String newKey) { key = newKey; } + @Override + public RedisOperations getOperations() { + return generalOps; + } + @Override public int intValue() { return (int) get(); @@ -418,4 +423,5 @@ public float floatValue() { public double doubleValue() { return get(); } + } diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java index ffdb6ff50d..a4f3e65dba 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicInteger.java @@ -399,6 +399,11 @@ public void rename(String newKey) { key = newKey; } + @Override + public RedisOperations getOperations() { + return generalOps; + } + @Override public int intValue() { return get(); diff --git a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java index df7f96035d..2e488697ae 100644 --- a/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java +++ b/src/main/java/org/springframework/data/redis/support/atomic/RedisAtomicLong.java @@ -396,6 +396,11 @@ public void rename(String newKey) { key = newKey; } + @Override + public RedisOperations getOperations() { + return generalOps; + } + @Override public int intValue() { return (int) get(); @@ -415,4 +420,5 @@ public float floatValue() { public double doubleValue() { return get(); } + } diff --git a/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java index 2bb8046f87..56f99db3ba 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java @@ -34,6 +34,7 @@ import org.springframework.data.redis.Person; import org.springframework.data.redis.SettingsUtils; import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.connection.ExpirationOptions; import org.springframework.data.redis.connection.RedisClusterConnection; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.StringRedisConnection; @@ -42,11 +43,13 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple; import org.springframework.data.redis.core.query.SortQueryBuilder; import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.serializer.GenericToStringSerializer; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.test.condition.EnabledIfLongRunningTest; +import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.extension.LettuceTestClientResources; import org.springframework.data.redis.test.extension.parametrized.MethodSource; import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest; @@ -503,6 +506,46 @@ void testExpireAndGetExpireMillis() { assertThat(redisTemplate.getExpire(key1, TimeUnit.MILLISECONDS)).isGreaterThan(0L); } + @ParameterizedRedisTest // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + void testBoundExpireAndGetExpireSeconds() { + + K key = keyFactory.instance(); + V value1 = valueFactory.instance(); + redisTemplate.boundValueOps(key).set(value1); + + BoundKeyExpirationOperations exp = redisTemplate.expiration(key); + + assertThat(exp.expire(Duration.ofSeconds(5))).isEqualTo(ExpireChanges.ExpiryChangeState.OK); + + assertThat(exp.getTimeToLive(TimeUnit.SECONDS)).satisfies(ttl -> { + assertThat(ttl.isPersistent()).isFalse(); + assertThat(ttl.value()).isGreaterThan(1); + }); + } + + @ParameterizedRedisTest // GH-3114 + @EnabledOnCommand("SPUBLISH") // Redis 7.0 + void testBoundExpireWithConditionsAndGetExpireSeconds() { + + K key = keyFactory.instance(); + V value1 = valueFactory.instance(); + redisTemplate.boundValueOps(key).set(value1); + + BoundKeyExpirationOperations exp = redisTemplate.expiration(key); + + assertThat(exp.expire(Duration.ofSeconds(5))).isEqualTo(ExpireChanges.ExpiryChangeState.OK); + assertThat(exp.expire(Expiration.from(Duration.ofSeconds(1)), ExpirationOptions.builder().gt().build())) + .isEqualTo(ExpireChanges.ExpiryChangeState.CONDITION_NOT_MET); + assertThat(exp.expire(Expiration.from(Duration.ofSeconds(10)), ExpirationOptions.builder().gt().build())) + .isEqualTo(ExpireChanges.ExpiryChangeState.OK); + + assertThat(exp.getTimeToLive(TimeUnit.SECONDS)).satisfies(ttl -> { + assertThat(ttl.isPersistent()).isFalse(); + assertThat(ttl.value()).isGreaterThan(5); + }); + } + @ParameterizedRedisTest void testGetExpireNoTimeUnit() { K key1 = keyFactory.instance(); From ce292f7fe5db11ac825b3f5956f1d18fc1f8dca6 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 10 Mar 2025 13:34:59 +0100 Subject: [PATCH 144/187] Polishing. Rename builder for ExpirationOptions, remove unused import and update javadoc. Original Pull Request: #3115 --- .../redis/connection/ExpirationOptions.java | 28 +++++++++++++------ .../redis/core/ReactiveRedisOperations.java | 2 +- .../data/redis/core/RedisOperations.java | 2 +- .../data/redis/core/types/Expiration.java | 1 - 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java b/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java index 2780b6149c..900f27bd9d 100644 --- a/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java +++ b/src/main/java/org/springframework/data/redis/connection/ExpirationOptions.java @@ -46,8 +46,8 @@ public static ExpirationOptions none() { /** * @return builder for creating {@code FieldExpireOptionsBuilder}. */ - public static FieldExpireOptionsBuilder builder() { - return new FieldExpireOptionsBuilder(); + public static ExpirationOptionsBuilder builder() { + return new ExpirationOptionsBuilder(); } public Condition getCondition() { @@ -74,32 +74,44 @@ public int hashCode() { /** * Builder to build {@link ExpirationOptions} */ - public static class FieldExpireOptionsBuilder { + public static class ExpirationOptionsBuilder { private Condition condition = Condition.ALWAYS; - private FieldExpireOptionsBuilder() {} + private ExpirationOptionsBuilder() {} + /** + * Apply to fields that have no expiration. + */ @Contract("-> this") - public FieldExpireOptionsBuilder nx() { + public ExpirationOptionsBuilder nx() { this.condition = Condition.NX; return this; } + /** + * Apply to fields that have an existing expiration. + */ @Contract("-> this") - public FieldExpireOptionsBuilder xx() { + public ExpirationOptionsBuilder xx() { this.condition = Condition.XX; return this; } + /** + * Apply to fields when the new expiration is greater than the current one. + */ @Contract("-> this") - public FieldExpireOptionsBuilder gt() { + public ExpirationOptionsBuilder gt() { this.condition = Condition.GT; return this; } + /** + * Apply to fields when the new expiration is lower than the current one. + */ @Contract("-> this") - public FieldExpireOptionsBuilder lt() { + public ExpirationOptionsBuilder lt() { this.condition = Condition.LT; return this; } diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java index 46faa18071..996ddbc584 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java @@ -382,7 +382,7 @@ default Flux scan() { * @param key must not be {@literal null}. * @param expiration must not be {@literal null}. * @param options must not be {@literal null}. - * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @throws IllegalArgumentException any of required arguments is {@literal null}. * @see Redis Documentation: EXPIRE * @see Redis Documentation: PEXPIRE * @see Redis Documentation: EXPIREAT diff --git a/src/main/java/org/springframework/data/redis/core/RedisOperations.java b/src/main/java/org/springframework/data/redis/core/RedisOperations.java index bb11c66a00..4ea682d900 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/RedisOperations.java @@ -375,7 +375,7 @@ default Boolean expireAt(K key, Instant expireAt) { * @param expiration must not be {@literal null}. * @param options must not be {@literal null}. * @return changes to the expiry. {@literal null} when used in pipeline / transaction. - * @throws IllegalArgumentException if the instant is {@literal null} or too large to represent as a {@code Date}. + * @throws IllegalArgumentException any of the required arguments is {@literal null}. * @see Redis Documentation: EXPIRE * @see Redis Documentation: PEXPIRE * @see Redis Documentation: EXPIREAT diff --git a/src/main/java/org/springframework/data/redis/core/types/Expiration.java b/src/main/java/org/springframework/data/redis/core/types/Expiration.java index a68dd516b8..2e4627dbc5 100644 --- a/src/main/java/org/springframework/data/redis/core/types/Expiration.java +++ b/src/main/java/org/springframework/data/redis/core/types/Expiration.java @@ -16,7 +16,6 @@ package org.springframework.data.redis.core.types; import java.time.Duration; -import java.util.Objects; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.TimeoutUtils; From 1f360e4837fd44e4be326353d23fbd5505eb92aa Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 12 Mar 2025 09:22:29 +0100 Subject: [PATCH 145/187] Upgrade to Lettuce 6.5.5.RELEASE. Also, bump Netty to 4.1.119.Final. Closes #3118 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fd73e23ebd..d5df70171f 100644 --- a/pom.xml +++ b/pom.xml @@ -24,10 +24,10 @@ 1.9.4 1.4.21 2.11.1 - 6.5.1.RELEASE + 6.5.5.RELEASE 5.2.0 1.01 - 4.1.115.Final + 4.1.119.Final spring.data.redis From d5555126b3079b8f90dfc32487e604356d7dc90b Mon Sep 17 00:00:00 2001 From: annemayor Date: Sun, 26 May 2024 23:19:05 +0900 Subject: [PATCH 146/187] =?UTF-8?q?Add=20`exists(=E2=80=A6)`=20accepting?= =?UTF-8?q?=20multiple=20keys=20to=20Reactive=20API.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2883 Original pull request: #2918 Signed-off-by: Anne Lee --- .../redis/connection/ReactiveKeyCommands.java | 8 ++++++ .../lettuce/LettuceReactiveKeyCommands.java | 8 ++++++ .../redis/core/ReactiveRedisOperations.java | 10 ++++++++ .../redis/core/ReactiveRedisTemplate.java | 20 +++++++++++++++ ...ceReactiveKeyCommandsIntegrationTests.java | 22 +++++++++++++--- ...ReactiveRedisTemplateIntegrationTests.java | 25 ++++++++++++++++--- 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java index 62be2393ef..249b23d41c 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java @@ -177,6 +177,14 @@ default Mono exists(ByteBuffer key) { return exists(Mono.just(new KeyCommand(key))).next().map(BooleanResponse::getOutput); } + /** + * Determine the number of given {@literal keys} that exist. + * + * @param keys must not be {@literal null} or {@literal empty}. + * @return {@link Mono} emitting {@literal the number of existing keys}. + * @see Redis Documentation: EXISTS + */ + Mono exists(List keys); /** * Determine if given {@literal key} exists. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java index 3dbb44c69d..5dde44bc6f 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java @@ -344,6 +344,14 @@ public Mono refcount(ByteBuffer key) { return connection.execute(cmd -> cmd.objectRefcount(key)).next(); } + @Override + public Mono exists(List keys) { + Assert.notNull(keys, "Key list must not be null"); + Assert.notEmpty(keys, "Key list must not be empty"); + + return connection.execute(cmd -> cmd.exists(keys.toArray(ByteBuffer[]::new))).next(); + } + private static ExpireArgs getExpireArgs(ExpirationOptions options) { return new ExpireArgs() { diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java index 996ddbc584..81c910ca57 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java @@ -22,6 +22,7 @@ import java.time.Duration; import java.time.Instant; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -421,6 +422,15 @@ default Flux scan() { */ Mono getExpire(K key); + /** + * Get the number of given {@code keys} that exists. + * + * @param keys must not be {@literal null} or {@literal empty}. + * @return the number of existing keys in redis. 0 if there are no existing keys. + * @see Redis Documentation: EXISTS + */ + Mono countExistingKeys(Collection keys); + // ------------------------------------------------------------------------- // Methods dealing with Redis Lua scripts // ------------------------------------------------------------------------- diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java index 0649072b4b..b6238bc3ea 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java @@ -23,6 +23,7 @@ import java.time.Duration; import java.time.Instant; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -505,6 +506,14 @@ public Mono move(K key, int dbIndex) { return doCreateMono(connection -> connection.keyCommands().move(rawKey(key), dbIndex)); } + @Override + public Mono countExistingKeys(Collection keys) { + Assert.notNull(keys, "Keys must not be null"); + + ByteBuffer[] rawKeys = rawKeys(keys); + return doCreateMono(connection -> connection.keyCommands().exists(Arrays.asList(rawKeys))); + } + // ------------------------------------------------------------------------- // Methods dealing with Redis Lua scripts // ------------------------------------------------------------------------- @@ -688,6 +697,17 @@ private ByteBuffer rawKey(K key) { return getSerializationContext().getKeySerializationPair().getWriter().write(key); } + private ByteBuffer[] rawKeys(Collection keys) { + final ByteBuffer[] rawKeys = new ByteBuffer[keys.size()]; + + int i = 0; + for (K key : keys) { + rawKeys[i++] = rawKey(key); + } + + return rawKeys; + } + @Nullable private K readKey(ByteBuffer buffer) { return getSerializationContext().getKeySerializationPair().getReader().read(buffer); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java index bc16ae8a46..d2bbc633d1 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java @@ -70,6 +70,22 @@ void existsShouldReturnFalseForNonExistingKeys() { connection.keyCommands().exists(KEY_1_BBUFFER).as(StepVerifier::create).expectNext(false).verifyComplete(); } + @ParameterizedRedisTest + void existsKeyReturnsKeyCount() { + nativeCommands.set(KEY_1, "1000"); + nativeCommands.set(KEY_2, "2000"); + nativeCommands.set(KEY_3, "3000"); + + connection.keyCommands().exists(List.of(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)).as(StepVerifier::create) + .expectNext(3L).verifyComplete(); + } + + @ParameterizedRedisTest + void existsKeyReturnsZeroWhenKeyDoesNotExist() { + connection.keyCommands().exists(List.of(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)).as(StepVerifier::create) + .expectNext(0L).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-525 void typeShouldReturnTypeCorrectly() { @@ -168,7 +184,7 @@ void renameShouldAlterKeyNameCorrectly() { connection.keyCommands().rename(KEY_1_BBUFFER, KEY_2_BBUFFER).as(StepVerifier::create).expectNext(true) .verifyComplete(); assertThat(nativeCommands.exists(KEY_2)).isEqualTo(1L); - assertThat(nativeCommands.exists(KEY_1)).isEqualTo(0L); + assertThat(nativeCommands.exists(KEY_1)).isZero(); } @ParameterizedRedisTest // DATAREDIS-525 @@ -187,7 +203,7 @@ void renameNXShouldAlterKeyNameCorrectly() { .verifyComplete(); assertThat(nativeCommands.exists(KEY_2)).isEqualTo(1L); - assertThat(nativeCommands.exists(KEY_1)).isEqualTo(0L); + assertThat(nativeCommands.exists(KEY_1)).isZero(); } @ParameterizedRedisTest // DATAREDIS-525 @@ -428,7 +444,7 @@ void shouldMoveToDatabase() { .expectNext(true) // .expectComplete() // .verify(); - assertThat(nativeCommands.exists(KEY_1)).isEqualTo(0L); + assertThat(nativeCommands.exists(KEY_1)).isZero(); } @ParameterizedRedisTest // DATAREDIS-694 diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java index f3c55deb55..40830cd466 100644 --- a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java @@ -24,10 +24,7 @@ import java.time.Duration; import java.time.Instant; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; @@ -582,4 +579,24 @@ void listenToPatternLaterShouldReceiveChannelMessagesCorrectly() { .thenCancel() // .verify(Duration.ofSeconds(3)); } + + @ParameterizedRedisTest + void countExistingKeysIfValidKeyExists() { + + K key = keyFactory.instance(); + K key2 = keyFactory.instance(); + K key3 = keyFactory.instance(); + + redisTemplate.opsForValue().set(key, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + redisTemplate.opsForValue().set(key2, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + redisTemplate.opsForValue().set(key3, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + + redisTemplate.countExistingKeys(Arrays.asList(key, key2, key3)).as(StepVerifier::create).expectNext(3L).verifyComplete(); + } + + @ParameterizedRedisTest + void countExistingKeysIfNotValidKeyExists() { + K key = keyFactory.instance(); + redisTemplate.countExistingKeys(List.of(key)).as(StepVerifier::create).expectNext(0L).verifyComplete(); + } } From ef5e735f3977a6e4fe553283bcdefad5b1b91736 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 12 Mar 2025 14:20:14 +0100 Subject: [PATCH 147/187] Polishing. Reformat code, add since and author tags. See #2883 Original pull request: #2918 --- .../redis/connection/ReactiveKeyCommands.java | 3 ++ .../lettuce/LettuceReactiveKeyCommands.java | 2 + .../redis/core/ReactiveRedisOperations.java | 20 +++---- .../redis/core/ReactiveRedisTemplate.java | 26 +++++----- ...ceReactiveKeyCommandsIntegrationTests.java | 8 +-- ...ReactiveRedisTemplateIntegrationTests.java | 52 ++++++++++++------- 6 files changed, 67 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java index 249b23d41c..3354cf9af1 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java @@ -42,6 +42,7 @@ * * @author Christoph Strobl * @author Mark Paluch + * @author Dahye Anne Lee * @since 2.0 */ public interface ReactiveKeyCommands { @@ -177,12 +178,14 @@ default Mono exists(ByteBuffer key) { return exists(Mono.just(new KeyCommand(key))).next().map(BooleanResponse::getOutput); } + /** * Determine the number of given {@literal keys} that exist. * * @param keys must not be {@literal null} or {@literal empty}. * @return {@link Mono} emitting {@literal the number of existing keys}. * @see Redis Documentation: EXISTS + * @since 3.5 */ Mono exists(List keys); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java index 5dde44bc6f..f4b7b8b916 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java @@ -48,6 +48,7 @@ /** * @author Christoph Strobl * @author Mark Paluch + * @author Dahye Anne Lee * @since 2.0 */ class LettuceReactiveKeyCommands implements ReactiveKeyCommands { @@ -346,6 +347,7 @@ public Mono refcount(ByteBuffer key) { @Override public Mono exists(List keys) { + Assert.notNull(keys, "Key list must not be null"); Assert.notEmpty(keys, "Key list must not be empty"); diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java index 81c910ca57..686277f0df 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java @@ -54,6 +54,7 @@ * * @author Mark Paluch * @author Christoph Strobl + * @author Dahye Anne Lee * @since 2.0 */ public interface ReactiveRedisOperations { @@ -243,6 +244,16 @@ default Mono>> listenToPatternLater(String... */ Mono hasKey(K key); + /** + * Get the number of given {@code keys} that exists. + * + * @param keys must not be {@literal null} or {@literal empty}. + * @return the number of existing keys in redis. 0 if there are no existing keys. + * @see Redis Documentation: EXISTS + * @since 3.5 + */ + Mono countExistingKeys(Collection keys); + /** * Determine the type stored at {@code key}. * @@ -422,15 +433,6 @@ default Flux scan() { */ Mono getExpire(K key); - /** - * Get the number of given {@code keys} that exists. - * - * @param keys must not be {@literal null} or {@literal empty}. - * @return the number of existing keys in redis. 0 if there are no existing keys. - * @see Redis Documentation: EXISTS - */ - Mono countExistingKeys(Collection keys); - // ------------------------------------------------------------------------- // Methods dealing with Redis Lua scripts // ------------------------------------------------------------------------- diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java index b6238bc3ea..3741aa751d 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java @@ -22,6 +22,7 @@ import java.nio.ByteBuffer; import java.time.Duration; import java.time.Instant; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -69,6 +70,7 @@ * @author Christoph Strobl * @author Petromir Dzhunev * @author John Blum + * @author Dahye Anne Lee * @param the Redis key type against which the template works (usually a String) * @param the Redis value type against which the template works * @since 2.0 @@ -326,6 +328,14 @@ public Mono hasKey(K key) { return doCreateMono(connection -> connection.keyCommands().exists(rawKey(key))); } + @Override + public Mono countExistingKeys(Collection keys) { + + Assert.notNull(keys, "Keys must not be null"); + + return doCreateMono(connection -> connection.keyCommands().exists(rawKeys(keys))); + } + @Override public Mono type(K key) { @@ -506,14 +516,6 @@ public Mono move(K key, int dbIndex) { return doCreateMono(connection -> connection.keyCommands().move(rawKey(key), dbIndex)); } - @Override - public Mono countExistingKeys(Collection keys) { - Assert.notNull(keys, "Keys must not be null"); - - ByteBuffer[] rawKeys = rawKeys(keys); - return doCreateMono(connection -> connection.keyCommands().exists(Arrays.asList(rawKeys))); - } - // ------------------------------------------------------------------------- // Methods dealing with Redis Lua scripts // ------------------------------------------------------------------------- @@ -697,12 +699,12 @@ private ByteBuffer rawKey(K key) { return getSerializationContext().getKeySerializationPair().getWriter().write(key); } - private ByteBuffer[] rawKeys(Collection keys) { - final ByteBuffer[] rawKeys = new ByteBuffer[keys.size()]; + private List rawKeys(Collection keys) { + + List rawKeys = new ArrayList<>(keys.size()); - int i = 0; for (K key : keys) { - rawKeys[i++] = rawKey(key); + rawKeys.add(rawKey(key)); } return rawKeys; diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java index d2bbc633d1..5dd4f5684b 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java @@ -50,6 +50,7 @@ * * @author Christoph Strobl * @author Mark Paluch + * @author Dahye Anne Lee */ public class LettuceReactiveKeyCommandsIntegrationTests extends LettuceReactiveCommandsTestSupport { @@ -70,8 +71,9 @@ void existsShouldReturnFalseForNonExistingKeys() { connection.keyCommands().exists(KEY_1_BBUFFER).as(StepVerifier::create).expectNext(false).verifyComplete(); } - @ParameterizedRedisTest + @ParameterizedRedisTest // GH-2883 void existsKeyReturnsKeyCount() { + nativeCommands.set(KEY_1, "1000"); nativeCommands.set(KEY_2, "2000"); nativeCommands.set(KEY_3, "3000"); @@ -80,8 +82,8 @@ void existsKeyReturnsKeyCount() { .expectNext(3L).verifyComplete(); } - @ParameterizedRedisTest - void existsKeyReturnsZeroWhenKeyDoesNotExist() { + @ParameterizedRedisTest // GH-2883 + void existsKeyReturnsZeroWhenKeysDoNotExist() { connection.keyCommands().exists(List.of(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)).as(StepVerifier::create) .expectNext(0L).verifyComplete(); } diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java index 40830cd466..f887c1ba22 100644 --- a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java @@ -24,10 +24,16 @@ import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; + import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.ObjectFactory; import org.springframework.data.redis.Person; @@ -60,6 +66,7 @@ * * @author Mark Paluch * @author Christoph Strobl + * @author Dahye Anne Lee */ @MethodSource("testParams") public class ReactiveRedisTemplateIntegrationTests { @@ -127,6 +134,30 @@ void exists() { redisTemplate.hasKey(key).as(StepVerifier::create).expectNext(true).verifyComplete(); } + @ParameterizedRedisTest // GH-2883 + void countExistingKeysIfValidKeyExists() { + + K key = keyFactory.instance(); + K key2 = keyFactory.instance(); + K key3 = keyFactory.instance(); + + ReactiveValueOperations ops = redisTemplate.opsForValue(); + + ops.set(key, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + ops.set(key2, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + ops.set(key3, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + + redisTemplate.countExistingKeys(Arrays.asList(key, key2, key3)).as(StepVerifier::create).expectNext(3L) + .verifyComplete(); + } + + @ParameterizedRedisTest // GH-2883 + void countExistingKeysIfNotValidKeyExists() { + + K key = keyFactory.instance(); + redisTemplate.countExistingKeys(List.of(key)).as(StepVerifier::create).expectNext(0L).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-743 void scan() { @@ -580,23 +611,4 @@ void listenToPatternLaterShouldReceiveChannelMessagesCorrectly() { .verify(Duration.ofSeconds(3)); } - @ParameterizedRedisTest - void countExistingKeysIfValidKeyExists() { - - K key = keyFactory.instance(); - K key2 = keyFactory.instance(); - K key3 = keyFactory.instance(); - - redisTemplate.opsForValue().set(key, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); - redisTemplate.opsForValue().set(key2, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); - redisTemplate.opsForValue().set(key3, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); - - redisTemplate.countExistingKeys(Arrays.asList(key, key2, key3)).as(StepVerifier::create).expectNext(3L).verifyComplete(); - } - - @ParameterizedRedisTest - void countExistingKeysIfNotValidKeyExists() { - K key = keyFactory.instance(); - redisTemplate.countExistingKeys(List.of(key)).as(StepVerifier::create).expectNext(0L).verifyComplete(); - } } From ba890c98e3e3d9267753419e5ff1c46cc0c12f42 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Mar 2025 09:30:46 +0100 Subject: [PATCH 148/187] Prepare 3.5 M2 (2025.0.0). See #3107 --- pom.xml | 16 +++------------- src/main/resources/notice.txt | 3 ++- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index d5df70171f..1fcf2876ab 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.5.0-SNAPSHOT + 3.5.0-M2 - 3.5.0-SNAPSHOT - 3.5.0-SNAPSHOT + 3.5.0-M2 + 3.5.0-M2 4.0.2 1.9.4 1.4.21 @@ -389,16 +389,6 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - spring-milestone https://repo.spring.io/milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 98fe9a6e5e..880bae215e 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 3.5 M1 (2025.0.0) +Spring Data Redis 3.5 M2 (2025.0.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -57,5 +57,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From b5775569534cf92a6d3c3ead4466ff30834f0ade Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Mar 2025 09:31:05 +0100 Subject: [PATCH 149/187] Release version 3.5 M2 (2025.0.0). See #3107 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1fcf2876ab..089fef9749 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.5.0-SNAPSHOT + 3.5.0-M2 Spring Data Redis Spring Data module for Redis From e4917849613ef4fa2a47eec50dfa9ba90fdab5b7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Mar 2025 09:33:42 +0100 Subject: [PATCH 150/187] Prepare next development iteration. See #3107 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 089fef9749..1fcf2876ab 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.5.0-M2 + 3.5.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From 7c9dd00540719b3412c003ab2bc3cd7f1f72e749 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Mar 2025 09:33:43 +0100 Subject: [PATCH 151/187] After release cleanups. See #3107 --- pom.xml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 1fcf2876ab..d5df70171f 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.5.0-M2 + 3.5.0-SNAPSHOT - 3.5.0-M2 - 3.5.0-M2 + 3.5.0-SNAPSHOT + 3.5.0-SNAPSHOT 4.0.2 1.9.4 1.4.21 @@ -389,6 +389,16 @@ + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + spring-milestone https://repo.spring.io/milestone From 73477919a68589265440487ef4fe0030a258ef02 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 14 Apr 2025 09:27:26 +0200 Subject: [PATCH 152/187] Polishing. Fix imports. Original Pull Request: #3115 --- .../support/collections/AbstractRedisMapIntegrationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java index f587339188..6532cdfdc5 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisMapIntegrationTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.redis.support.collections; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.*; +import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assumptions.*; import java.io.IOException; From 42c5a5452bfcf67d41d456688e4dbcc90b520877 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 17 Apr 2025 09:44:50 +0200 Subject: [PATCH 153/187] Remove custom `awaitility` in favor of Build-managed versions. Closes #3132 --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index d5df70171f..9b13e4e4a3 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,6 @@ 3.5.0-SNAPSHOT 3.5.0-SNAPSHOT - 4.0.2 1.9.4 1.4.21 2.11.1 From 0de3af0ac5dd254492aed934ca49ebcfbc615351 Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Wed, 16 Apr 2025 14:39:40 +0900 Subject: [PATCH 154/187] Refine `Topic` creation for `ChannelTopic` and `PatternTopic`. Signed-off-by: jonghoonpark Closes #3131 --- src/main/antora/modules/ROOT/pages/redis/pubsub.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc b/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc index 6539ca127c..736683835a 100644 --- a/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc @@ -162,7 +162,7 @@ XML:: ---- ====== -NOTE: The listener topic can be either a channel (for example, `topic="chatroom"`) or a pattern (for example, `topic="*room"`) +NOTE: The listener topic can be either a channel (for example, `topic="chatroom"`) or a pattern (for example, `topic="*room"`). For channels, you should use the `ChannelTopic` class, and for patterns, use the `PatternTopic` class. The preceding example uses the Redis namespace to declare the message listener container and automatically register the POJOs as listeners. The full-blown beans definition follows: From 502909496701a2da54d1080bbab901f9faf5fca7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 17 Apr 2025 15:51:02 +0200 Subject: [PATCH 155/187] Refine `Topic` creation for `ChannelTopic` and `PatternTopic`. We now expose factory methods to construct ChannelTopic and PatternTopic from the Topic interface. See #3131 --- .../modules/ROOT/pages/redis/pubsub.adoc | 2 +- .../data/redis/listener/Topic.java | 24 +++++++++++++++++++ ...sageListenerContainerIntegrationTests.java | 13 ++++------ ...edisMessageListenerContainerUnitTests.java | 22 ++++++++--------- ...ctiveRedisOperationsExtensionsUnitTests.kt | 6 ++--- 5 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc b/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc index 736683835a..031425a4ed 100644 --- a/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/pubsub.adoc @@ -162,7 +162,7 @@ XML:: ---- ====== -NOTE: The listener topic can be either a channel (for example, `topic="chatroom"`) or a pattern (for example, `topic="*room"`). For channels, you should use the `ChannelTopic` class, and for patterns, use the `PatternTopic` class. +NOTE: The listener topic can be either a channel (for example, `topic="chatroom"` respective `Topic.channel("chatroom")`) or a pattern (for example, `topic="*room"` respective `Topic.pattern("*room")`). The preceding example uses the Redis namespace to declare the message listener container and automatically register the POJOs as listeners. The full-blown beans definition follows: diff --git a/src/main/java/org/springframework/data/redis/listener/Topic.java b/src/main/java/org/springframework/data/redis/listener/Topic.java index 0aad48207a..bec4cd3c32 100644 --- a/src/main/java/org/springframework/data/redis/listener/Topic.java +++ b/src/main/java/org/springframework/data/redis/listener/Topic.java @@ -19,13 +19,37 @@ * Topic for a Redis message. Acts a high-level abstraction on top of Redis low-level channels or patterns. * * @author Costin Leau + * @author Mark Paluch */ public interface Topic { + /** + * Create a new {@link ChannelTopic} for channel subscriptions. + * + * @param channelName {@link String name} of the Redis channel; must not be {@literal null}. + * @return the {@link ChannelTopic} for the given {@code channelName}. + * @since 3.5 + */ + static ChannelTopic channel(String channelName) { + return ChannelTopic.of(channelName); + } + + /** + * Create a new {@link PatternTopic} for channel subscriptions based on a {@code pattern}. + * + * @param pattern {@link String pattern} used to match channels; must not be {@literal null} or empty. + * @return the {@link PatternTopic} for the given {@code pattern}. + * @since 3.5 + */ + static PatternTopic pattern(String pattern) { + return PatternTopic.of(pattern); + } + /** * Returns the topic (as a String). * * @return the topic */ String getTopic(); + } diff --git a/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerIntegrationTests.java b/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerIntegrationTests.java index e990cea5f2..4b59bc872d 100644 --- a/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerIntegrationTests.java @@ -23,12 +23,9 @@ import java.nio.ByteBuffer; import java.time.Duration; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.List; -import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.LinkedBlockingDeque; @@ -110,7 +107,7 @@ void shouldReceiveChannelMessages() { ReactiveRedisMessageListenerContainer container = new ReactiveRedisMessageListenerContainer(connectionFactory); - container.receiveLater(ChannelTopic.of(CHANNEL1)) // + container.receiveLater(Topic.channel(CHANNEL1)) // .doOnNext(it -> doPublish(CHANNEL1.getBytes(), MESSAGE.getBytes())) // .flatMapMany(Function.identity()) // .as(StepVerifier::create) // @@ -153,7 +150,7 @@ public void onChannelUnsubscribed(byte[] channel, long count) { } }; - container.receive(Collections.singletonList(ChannelTopic.of(CHANNEL1)), listener) // + container.receive(Collections.singletonList(Topic.channel(CHANNEL1)), listener) // .as(StepVerifier::create) // .then(awaitSubscription(container::getActiveSubscriptions)) .then(() -> doPublish(CHANNEL1.getBytes(), MESSAGE.getBytes())) // @@ -220,7 +217,7 @@ public void onPatternUnsubscribed(byte[] pattern, long count) { } }; - container.receive(Collections.singletonList(PatternTopic.of(PATTERN1)), listener) // + container.receive(Collections.singletonList(Topic.pattern(PATTERN1)), listener) // .cast(PatternMessage.class) // .as(StepVerifier::create) // .then(awaitSubscription(container::getActiveSubscriptions)) @@ -314,10 +311,10 @@ void multipleListenShouldTrackSubscriptions() throws Exception { ReactiveRedisMessageListenerContainer container = new ReactiveRedisMessageListenerContainer(connectionFactory); - Flux> c1 = container.receiveLater(ChannelTopic.of(CHANNEL1)) + Flux> c1 = container.receiveLater(Topic.channel(CHANNEL1)) .block(); Flux> c1p1 = container - .receiveLater(Arrays.asList(ChannelTopic.of(CHANNEL1), PatternTopic.of(PATTERN1)), + .receiveLater(Arrays.asList(Topic.channel(CHANNEL1), PatternTopic.of(PATTERN1)), SerializationPair.fromSerializer(RedisSerializer.string()), SerializationPair.fromSerializer(RedisSerializer.string())) .block(); diff --git a/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerUnitTests.java b/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerUnitTests.java index f5e99bff47..f22ff8e560 100644 --- a/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/listener/ReactiveRedisMessageListenerContainerUnitTests.java @@ -79,7 +79,7 @@ void shouldSubscribeToPattern() { container = createContainer(); - container.receive(PatternTopic.of("foo*")).as(StepVerifier::create).thenAwait().thenCancel().verify(); + container.receive(Topic.pattern("foo*")).as(StepVerifier::create).thenAwait().thenCancel().verify(); verify(subscriptionMock).pSubscribe(getByteBuffer("foo*")); } @@ -90,7 +90,7 @@ void shouldSubscribeToMultiplePatterns() { when(subscriptionMock.receive()).thenReturn(Flux.never()); container = createContainer(); - container.receive(PatternTopic.of("foo*"), PatternTopic.of("bar*")).as(StepVerifier::create).thenRequest(1) + container.receive(Topic.pattern("foo*"), Topic.pattern("bar*")).as(StepVerifier::create).thenRequest(1) .thenAwait().thenCancel().verify(); verify(subscriptionMock).pSubscribe(getByteBuffer("foo*"), getByteBuffer("bar*")); @@ -102,7 +102,7 @@ void shouldSubscribeToChannel() { when(subscriptionMock.receive()).thenReturn(Flux.never()); container = createContainer(); - container.receive(ChannelTopic.of("foo")).as(StepVerifier::create).thenAwait().thenCancel().verify(); + container.receive(Topic.channel("foo")).as(StepVerifier::create).thenAwait().thenCancel().verify(); verify(subscriptionMock).subscribe(getByteBuffer("foo")); } @@ -113,7 +113,7 @@ void shouldSubscribeToMultipleChannels() { when(subscriptionMock.receive()).thenReturn(Flux.never()); container = createContainer(); - container.receive(ChannelTopic.of("foo"), ChannelTopic.of("bar")).as(StepVerifier::create).thenAwait().thenCancel() + container.receive(Topic.channel("foo"), Topic.channel("bar")).as(StepVerifier::create).thenAwait().thenCancel() .verify(); verify(subscriptionMock).subscribe(getByteBuffer("foo"), getByteBuffer("bar")); @@ -127,7 +127,7 @@ void shouldEmitChannelMessage() { when(subscriptionMock.receive()).thenReturn(sink.asFlux()); container = createContainer(); - Flux> messageStream = container.receive(ChannelTopic.of("foo")); + Flux> messageStream = container.receive(Topic.channel("foo")); messageStream.as(StepVerifier::create).then(() -> { sink.tryEmitNext(createChannelMessage("foo", "message")); @@ -146,7 +146,7 @@ void shouldEmitPatternMessage() { when(subscriptionMock.receive()).thenReturn(sink.asFlux()); container = createContainer(); - Flux> messageStream = container.receive(PatternTopic.of("foo*")); + Flux> messageStream = container.receive(Topic.pattern("foo*")); messageStream.as(StepVerifier::create).then(() -> { sink.tryEmitNext(createPatternMessage("foo*", "foo", "message")); @@ -171,7 +171,7 @@ void shouldRegisterSubscription() { when(subscriptionMock.receive()).thenReturn(sink.asFlux()); container = createContainer(); - Flux> messageStream = container.receive(ChannelTopic.of("foo*")); + Flux> messageStream = container.receive(Topic.channel("foo*")); Disposable subscription = messageStream.subscribe(); @@ -193,7 +193,7 @@ void shouldRegisterSubscriptionMultipleSubscribers() { when(subscriptionMock.receive()).thenReturn(sink.asFlux()); container = createContainer(); - Flux> messageStream = container.receive(new ChannelTopic("foo*")); + Flux> messageStream = container.receive(Topic.channel("foo*")); Disposable first = messageStream.subscribe(); Disposable second = messageStream.subscribe(); @@ -216,7 +216,7 @@ void shouldUnsubscribeOnCancel() { when(subscriptionMock.receive()).thenReturn(sink.asFlux()); container = createContainer(); - Flux> messageStream = container.receive(PatternTopic.of("foo*")); + Flux> messageStream = container.receive(Topic.pattern("foo*")); messageStream.as(StepVerifier::create).then(() -> { @@ -240,7 +240,7 @@ void shouldTerminateSubscriptionsOnShutdown() { })); container = createContainer(); - Flux> messageStream = container.receive(PatternTopic.of("foo*")); + Flux> messageStream = container.receive(Topic.pattern("foo*")); messageStream.as(StepVerifier::create).then(() -> { container.destroy(); @@ -255,7 +255,7 @@ void shouldCleanupDownstream() { when(subscriptionMock.receive()).thenReturn(sink.asFlux()); container = createContainer(); - Flux> messageStream = container.receive(PatternTopic.of("foo*")); + Flux> messageStream = container.receive(Topic.pattern("foo*")); messageStream.as(StepVerifier::create).then(() -> { assertThat(sink.currentSubscriberCount()).isGreaterThan(0); diff --git a/src/test/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensionsUnitTests.kt b/src/test/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensionsUnitTests.kt index 62a6c968f3..90563f411f 100644 --- a/src/test/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensionsUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/redis/core/ReactiveRedisOperationsExtensionsUnitTests.kt @@ -26,7 +26,7 @@ import org.junit.jupiter.api.Test import org.springframework.data.redis.connection.DataType import org.springframework.data.redis.connection.ReactiveSubscription import org.springframework.data.redis.core.script.RedisScript -import org.springframework.data.redis.listener.ChannelTopic +import org.springframework.data.redis.listener.Topic import org.springframework.data.redis.serializer.RedisElementReader import org.springframework.data.redis.serializer.RedisElementWriter import reactor.core.publisher.Flux @@ -167,8 +167,8 @@ class ReactiveRedisOperationsExtensionsUnitTests { @Test // DATAREDIS-1033 fun listenTo() { - val topic1 = ChannelTopic.of("foo") - val topic2 = ChannelTopic.of("bar") + val topic1 = Topic.channel("foo") + val topic2 = Topic.channel("bar") val message = ReactiveSubscription.ChannelMessage("a", "b") val operations = mockk>() every { operations.listenTo(any(), any()) } returns Flux.just(message) From 2ac9c96e254e2db8f1461ad7eae27a13db374843 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Apr 2025 11:23:52 +0200 Subject: [PATCH 156/187] Prepare 3.5 RC1 (2025.0.0). See #3120 --- pom.xml | 16 +++------------- src/main/resources/notice.txt | 3 ++- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 9b13e4e4a3..d93ee1c4e2 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.5.0-SNAPSHOT + 3.5.0-RC1 - 3.5.0-SNAPSHOT - 3.5.0-SNAPSHOT + 3.5.0-RC1 + 3.5.0-RC1 1.9.4 1.4.21 2.11.1 @@ -388,16 +388,6 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - spring-milestone https://repo.spring.io/milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 880bae215e..42ea89ab38 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 3.5 M2 (2025.0.0) +Spring Data Redis 3.5 RC1 (2025.0.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -58,5 +58,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From 68644d1de409355a47f8526d7a4c15ef3bf09477 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Apr 2025 11:24:11 +0200 Subject: [PATCH 157/187] Release version 3.5 RC1 (2025.0.0). See #3120 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d93ee1c4e2..06979b1436 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.5.0-SNAPSHOT + 3.5.0-RC1 Spring Data Redis Spring Data module for Redis From d1b3c526d85042514ab6a92a59d960ae3b80023e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Apr 2025 11:26:48 +0200 Subject: [PATCH 158/187] Prepare next development iteration. See #3120 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 06979b1436..d93ee1c4e2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.5.0-RC1 + 3.5.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From f4191e6b70defa41aa5744b03e71eac31173fa13 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Apr 2025 11:26:49 +0200 Subject: [PATCH 159/187] After release cleanups. See #3120 --- pom.xml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d93ee1c4e2..9b13e4e4a3 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.5.0-RC1 + 3.5.0-SNAPSHOT - 3.5.0-RC1 - 3.5.0-RC1 + 3.5.0-SNAPSHOT + 3.5.0-SNAPSHOT 1.9.4 1.4.21 2.11.1 @@ -388,6 +388,16 @@ + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + spring-milestone https://repo.spring.io/milestone From 4c6ff0299e29ec6186c6d19e3a4c42e7f8e49f58 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 12 May 2025 08:55:54 +0200 Subject: [PATCH 160/187] Update CI Properties. See #3136 --- .mvn/jvm.config | 4 ++++ ci/pipeline.properties | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.mvn/jvm.config b/.mvn/jvm.config index 32599cefea..e27f6e8f5e 100644 --- a/.mvn/jvm.config +++ b/.mvn/jvm.config @@ -8,3 +8,7 @@ --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED +--add-opens=java.base/java.util=ALL-UNNAMED +--add-opens=java.base/java.lang.reflect=ALL-UNNAMED +--add-opens=java.base/java.text=ALL-UNNAMED +--add-opens=java.desktop/java.awt.font=ALL-UNNAMED diff --git a/ci/pipeline.properties b/ci/pipeline.properties index cd2fcf7fbe..9eb163fde7 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -7,8 +7,6 @@ docker.java.main.image=library/eclipse-temurin:${java.main.tag} docker.java.next.image=library/eclipse-temurin:${java.next.tag} # Supported versions of MongoDB -docker.mongodb.4.4.version=4.4.25 -docker.mongodb.5.0.version=5.0.21 docker.mongodb.6.0.version=6.0.10 docker.mongodb.7.0.version=7.0.2 docker.mongodb.8.0.version=8.0.0 @@ -17,9 +15,6 @@ docker.mongodb.8.0.version=8.0.0 docker.redis.6.version=6.2.13 docker.redis.7.version=7.2.4 -# Supported versions of Cassandra -docker.cassandra.3.version=3.11.16 - # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home From df86f7b3b25a44defb3b9ba70ca9d45e0afba2b3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 12 May 2025 08:56:22 +0200 Subject: [PATCH 161/187] Update CI Properties. See #3136 --- ci/pipeline.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 9eb163fde7..34eef52b6f 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,15 +1,15 @@ # Java versions java.main.tag=17.0.13_11-jdk-focal -java.next.tag=23.0.1_11-jdk-noble +java.next.tag=24.0.1_9-jdk-noble # Docker container images - standard docker.java.main.image=library/eclipse-temurin:${java.main.tag} docker.java.next.image=library/eclipse-temurin:${java.next.tag} # Supported versions of MongoDB -docker.mongodb.6.0.version=6.0.10 -docker.mongodb.7.0.version=7.0.2 -docker.mongodb.8.0.version=8.0.0 +docker.mongodb.6.0.version=6.0.23 +docker.mongodb.7.0.version=7.0.20 +docker.mongodb.8.0.version=8.0.9 # Supported versions of Redis docker.redis.6.version=6.2.13 From 5f49c074a33fc00b3f4c7ee82c49293f3fbee64d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 12 May 2025 09:00:23 +0200 Subject: [PATCH 162/187] Update CI Properties. See #3136 --- ci/pipeline.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 34eef52b6f..cb3670dee1 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,5 +1,5 @@ # Java versions -java.main.tag=17.0.13_11-jdk-focal +java.main.tag=17.0.15_6-jdk-focal java.next.tag=24.0.1_9-jdk-noble # Docker container images - standard From 12941af141abe494cae58347ebfd936a25e0c28c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 12 May 2025 09:33:06 +0200 Subject: [PATCH 163/187] Update CI Properties. See #3136 --- ci/pipeline.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index cb3670dee1..8dd2295acc 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -14,6 +14,7 @@ docker.mongodb.8.0.version=8.0.9 # Supported versions of Redis docker.redis.6.version=6.2.13 docker.redis.7.version=7.2.4 +docker.valkey.8.version=8.1.1 # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home From 13343a00fbd2820d23fa55fcfd20f99d7f85c699 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 12 May 2025 10:53:10 +0200 Subject: [PATCH 164/187] Polishing. Add missing packages. See #3136 --- ci/openjdk17-redis-6.2/Dockerfile | 2 +- ci/openjdk17-redis-7.2/Dockerfile | 2 +- ci/openjdk17-valkey-8.0/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/openjdk17-redis-6.2/Dockerfile b/ci/openjdk17-redis-6.2/Dockerfile index bf7cb4d390..8704997bf6 100644 --- a/ci/openjdk17-redis-6.2/Dockerfile +++ b/ci/openjdk17-redis-6.2/Dockerfile @@ -13,7 +13,7 @@ RUN set -eux; \ sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \ sed -i -e 's/http/https/g' /etc/apt/sources.list && \ apt-get update ; \ - apt-get install -y build-essential ; \ + apt-get install -y build-essential curl; \ make work/redis/bin/redis-cli work/redis/bin/redis-server VERSION=${VERSION}; \ chmod -R o+rw work; \ apt-get clean; \ diff --git a/ci/openjdk17-redis-7.2/Dockerfile b/ci/openjdk17-redis-7.2/Dockerfile index bf7cb4d390..8704997bf6 100644 --- a/ci/openjdk17-redis-7.2/Dockerfile +++ b/ci/openjdk17-redis-7.2/Dockerfile @@ -13,7 +13,7 @@ RUN set -eux; \ sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \ sed -i -e 's/http/https/g' /etc/apt/sources.list && \ apt-get update ; \ - apt-get install -y build-essential ; \ + apt-get install -y build-essential curl; \ make work/redis/bin/redis-cli work/redis/bin/redis-server VERSION=${VERSION}; \ chmod -R o+rw work; \ apt-get clean; \ diff --git a/ci/openjdk17-valkey-8.0/Dockerfile b/ci/openjdk17-valkey-8.0/Dockerfile index 2c7dfcbce4..5b040438ea 100644 --- a/ci/openjdk17-valkey-8.0/Dockerfile +++ b/ci/openjdk17-valkey-8.0/Dockerfile @@ -15,7 +15,7 @@ RUN set -eux; \ sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \ sed -i -e 's/http/https/g' /etc/apt/sources.list && \ apt-get update ; \ - apt-get install -y build-essential ; \ + apt-get install -y build-essential curl; \ make work/valkey/bin/valkey-cli work/valkey/bin/valkey-server VERSION=${VERSION}; \ chmod -R o+rw work; \ apt-get clean; \ From 5d968c94bf0253dfce3343388dfb7be9b6a3a0ed Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 12 May 2025 11:52:30 +0200 Subject: [PATCH 165/187] Fix broken Javadoc. Closes #3148 --- .../redis/cache/RedisCacheConfiguration.java | 6 +- .../data/redis/cache/RedisCacheWriter.java | 12 ++-- .../connection/ClusterCommandExecutor.java | 4 +- .../connection/ReactiveZSetCommands.java | 2 +- .../data/redis/connection/ReturnType.java | 4 +- .../connection/jedis/JedisConnection.java | 57 +++++++------------ .../lettuce/LettuceConnectionFactory.java | 6 +- .../stream/StreamSerialization.java | 4 +- .../redis/connection/util/DecodeUtils.java | 4 +- .../redis/connection/zset/DefaultTuple.java | 2 +- .../redis/core/ReactiveListOperations.java | 12 ++-- .../redis/core/ReactiveZSetOperations.java | 8 +-- .../data/redis/core/RedisConnectionUtils.java | 2 +- .../data/redis/core/RedisKeyValueAdapter.java | 4 +- .../data/redis/core/convert/Bucket.java | 2 +- .../serializer/RedisSerializationContext.java | 2 +- .../redis/serializer/RedisSerializer.java | 2 +- ...sSerializerToSerializationPairAdapter.java | 2 +- .../serializer/StringRedisSerializer.java | 2 +- .../StreamMessageListenerContainer.java | 2 +- .../data/redis/stream/StreamReceiver.java | 6 +- .../support/collections/package-info.java | 2 +- 22 files changed, 65 insertions(+), 82 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java index 56022f81f9..b74ebf2d75 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java @@ -437,12 +437,12 @@ public void configureKeyConverters(Consumer registryConsumer) *

      * The following converters get registered: *

        - *
      • {@link String} to {@link byte byte[]} using UTF-8 encoding.
      • + *
      • {@link String} to {@code byte byte[]} using UTF-8 encoding.
      • *
      • {@link SimpleKey} to {@link String}
      • *
      * - * @param registry {@link ConverterRegistry} in which the {@link Converter key converters} are registered; - * must not be {@literal null}. + * @param registry {@link ConverterRegistry} in which the {@link Converter key converters} are registered; must not be + * {@literal null}. * @see org.springframework.core.convert.converter.ConverterRegistry */ public static void registerDefaultConverters(ConverterRegistry registry) { diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java index 87339d66a2..e5e3d16b2b 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java @@ -182,13 +182,13 @@ default boolean supportsAsyncRetrieve() { /** * Asynchronously retrieves the {@link CompletableFuture value} to which the {@link RedisCache} maps the given - * {@link byte[] key}. + * {@code byte[] key}. *

      * This operation is non-blocking. * * @param name {@link String} with the name of the {@link RedisCache}. - * @param key {@link byte[] key} mapped to the {@link CompletableFuture value} in the {@link RedisCache}. - * @return the {@link CompletableFuture value} to which the {@link RedisCache} maps the given {@link byte[] key}. + * @param key {@code byte[] key} mapped to the {@link CompletableFuture value} in the {@link RedisCache}. + * @return the {@link CompletableFuture value} to which the {@link RedisCache} maps the given {@code byte[] key}. * @see #retrieve(String, byte[], Duration) * @since 3.2 */ @@ -198,14 +198,14 @@ default CompletableFuture retrieve(String name, byte[] key) { /** * Asynchronously retrieves the {@link CompletableFuture value} to which the {@link RedisCache} maps the given - * {@link byte[] key} setting the {@link Duration TTL expiration} for the cache entry. + * {@code byte[] key} setting the {@link Duration TTL expiration} for the cache entry. *

      * This operation is non-blocking. * * @param name {@link String} with the name of the {@link RedisCache}. - * @param key {@link byte[] key} mapped to the {@link CompletableFuture value} in the {@link RedisCache}. + * @param key {@code byte[] key} mapped to the {@link CompletableFuture value} in the {@link RedisCache}. * @param ttl {@link Duration} specifying the {@literal expiration timeout} for the cache entry. - * @return the {@link CompletableFuture value} to which the {@link RedisCache} maps the given {@link byte[] key}. + * @return the {@link CompletableFuture value} to which the {@link RedisCache} maps the given {@code byte[] key}. * @since 3.2 */ CompletableFuture retrieve(String name, byte[] key, @Nullable Duration ttl); diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java index 51c8c9372d..b0d9c1a720 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java @@ -471,9 +471,9 @@ public RedisClusterNode getNode() { } /** - * Return the {@link byte[] key} mapped to the value stored in Redis. + * Return the {@code byte[] key} mapped to the value stored in Redis. * - * @return a {@link byte[] byte array} of the key mapped to the value stored in Redis. + * @return a {@code byte[] byte array} of the key mapped to the value stored in Redis. */ public byte[] getKey() { return this.key.getArray(); diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java index 5400250eb9..c4037e2c55 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java @@ -1974,7 +1974,7 @@ default Mono zCard(ByteBuffer key) { } /** - * Get the size of sorted set with {@linByteBuffer keyCommand#getKey()}. + * Get the size of sorted set with {@link ByteBuffer keyCommand#getKey()}. * * @param commands must not be {@literal null}. * @return diff --git a/src/main/java/org/springframework/data/redis/connection/ReturnType.java b/src/main/java/org/springframework/data/redis/connection/ReturnType.java index 5085e6e6bd..b39a9684ce 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReturnType.java +++ b/src/main/java/org/springframework/data/redis/connection/ReturnType.java @@ -46,12 +46,12 @@ public enum ReturnType { MULTI, /** - * Returned as {@literal byte[]} + * Returned as {@code byte[]} */ STATUS, /** - * Returned as {@literal byte[]} + * Returned as {@code byte[]} */ VALUE; 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 3ec675dfdd..5888498d2b 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 @@ -15,6 +15,21 @@ */ package org.springframework.data.redis.connection.jedis; +import redis.clients.jedis.BuilderFactory; +import redis.clients.jedis.CommandArguments; +import redis.clients.jedis.CommandObject; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.Pipeline; +import redis.clients.jedis.Response; +import redis.clients.jedis.Transaction; +import redis.clients.jedis.commands.ProtocolCommand; +import redis.clients.jedis.commands.ServerCommands; +import redis.clients.jedis.exceptions.JedisDataException; +import redis.clients.jedis.util.Pool; + import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -26,31 +41,14 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.convert.converter.Converter; 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.RedisSystemException; -import org.springframework.data.redis.connection.AbstractRedisConnection; -import org.springframework.data.redis.connection.FutureResult; -import org.springframework.data.redis.connection.MessageListener; -import org.springframework.data.redis.connection.RedisCommands; -import org.springframework.data.redis.connection.RedisGeoCommands; -import org.springframework.data.redis.connection.RedisHashCommands; -import org.springframework.data.redis.connection.RedisHyperLogLogCommands; -import org.springframework.data.redis.connection.RedisKeyCommands; -import org.springframework.data.redis.connection.RedisListCommands; -import org.springframework.data.redis.connection.RedisNode; -import org.springframework.data.redis.connection.RedisPipelineException; -import org.springframework.data.redis.connection.RedisScriptingCommands; -import org.springframework.data.redis.connection.RedisServerCommands; -import org.springframework.data.redis.connection.RedisSetCommands; -import org.springframework.data.redis.connection.RedisStreamCommands; -import org.springframework.data.redis.connection.RedisStringCommands; -import org.springframework.data.redis.connection.RedisSubscribedConnectionException; -import org.springframework.data.redis.connection.RedisZSetCommands; -import org.springframework.data.redis.connection.Subscription; +import org.springframework.data.redis.connection.*; import org.springframework.data.redis.connection.convert.TransactionResultConverter; import org.springframework.data.redis.connection.jedis.JedisInvoker.ResponseCommands; import org.springframework.data.redis.connection.jedis.JedisResult.JedisResultBuilder; @@ -59,21 +57,6 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; -import redis.clients.jedis.BuilderFactory; -import redis.clients.jedis.CommandArguments; -import redis.clients.jedis.CommandObject; -import redis.clients.jedis.DefaultJedisClientConfig; -import redis.clients.jedis.HostAndPort; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisClientConfig; -import redis.clients.jedis.Pipeline; -import redis.clients.jedis.Response; -import redis.clients.jedis.Transaction; -import redis.clients.jedis.commands.ProtocolCommand; -import redis.clients.jedis.commands.ServerCommands; -import redis.clients.jedis.exceptions.JedisDataException; -import redis.clients.jedis.util.Pool; - /** * {@code RedisConnection} implementation on top of Jedis library. *

      @@ -148,7 +131,7 @@ public JedisConnection(Jedis jedis) { } /** - * Constructs a new <{@link JedisConnection} backed by a Jedis {@link Pool}. + * Constructs a new {@link JedisConnection} backed by a Jedis {@link Pool}. * * @param jedis {@link Jedis} client. * @param pool {@link Pool} of Redis connections; can be null, if no pool is used. @@ -159,7 +142,7 @@ public JedisConnection(Jedis jedis, Pool pool, int dbIndex) { } /** - * Constructs a new <{@link JedisConnection} backed by a Jedis {@link Pool}. + * Constructs a new {@link JedisConnection} backed by a Jedis {@link Pool}. * * @param jedis {@link Jedis} client. * @param pool {@link Pool} of Redis connections; can be null, if no pool is used. @@ -172,7 +155,7 @@ protected JedisConnection(Jedis jedis, @Nullable Pool pool, int dbIndex, } /** - * Constructs a new <{@link JedisConnection} backed by a Jedis {@link Pool}. + * Constructs a new {@link JedisConnection} backed by a Jedis {@link Pool}. * * @param jedis {@link Jedis} client. * @param pool {@link Pool} of Redis connections; can be null, if no pool is used. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java index 9bb8644a9a..befdfffe50 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java @@ -565,7 +565,7 @@ public void setShareNativeConnection(boolean shareNativeConnection) { * connection factory configuration. Eager initialization also prevents blocking connect while using reactive API and * is recommended for reactive API usage. * - * @return {@link true} if the shared connection is initialized upon {@link #start()}. + * @return {@literal true} if the shared connection is initialized upon {@link #start()}. * @since 2.2 * @see #start() */ @@ -1239,7 +1239,7 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { } /** - * @return the shared connection using {@literal byte[]} encoding for imperative API use. {@literal null} if + * @return the shared connection using {@code byte[]} encoding for imperative API use. {@literal null} if * {@link #getShareNativeConnection() connection sharing} is disabled or when connected to Redis Cluster. */ @Nullable @@ -1251,7 +1251,7 @@ protected StatefulRedisConnection getSharedConnection() { } /** - * @return the shared cluster connection using {@literal byte[]} encoding for imperative API use. {@literal null} if + * @return the shared cluster connection using {@code byte[]} encoding for imperative API use. {@literal null} if * {@link #getShareNativeConnection() connection sharing} is disabled or when connected to Redis * Standalone/Sentinel/Master-Replica. * @since 2.5.7 diff --git a/src/main/java/org/springframework/data/redis/connection/stream/StreamSerialization.java b/src/main/java/org/springframework/data/redis/connection/stream/StreamSerialization.java index 6770bb3b77..8849fc9813 100644 --- a/src/main/java/org/springframework/data/redis/connection/stream/StreamSerialization.java +++ b/src/main/java/org/springframework/data/redis/connection/stream/StreamSerialization.java @@ -59,12 +59,12 @@ static T deserialize(@Nullable RedisSerializer serializer, byte } /** - * Returns whether the given {@link RedisSerializer} is capable of serializing the {@code value} to {@literal byte[]}. + * Returns whether the given {@link RedisSerializer} is capable of serializing the {@code value} to {@code byte[]}. * * @param serializer the serializer. Can be {@literal null}. * @param value the value to serialize. * @return {@literal true} if the given {@link RedisSerializer} is capable of serializing the {@code value} to - * {@literal byte[]}. + * {@code byte[]}. */ private static boolean canSerialize(@Nullable RedisSerializer serializer, @Nullable Object value) { return serializer != null && (value == null || serializer.canSerialize(value.getClass())); diff --git a/src/main/java/org/springframework/data/redis/connection/util/DecodeUtils.java b/src/main/java/org/springframework/data/redis/connection/util/DecodeUtils.java index 9e688183d2..42e7bd43a6 100644 --- a/src/main/java/org/springframework/data/redis/connection/util/DecodeUtils.java +++ b/src/main/java/org/springframework/data/redis/connection/util/DecodeUtils.java @@ -30,8 +30,8 @@ * Simple class containing various decoding utilities. * * @author Costin Leau - * @auhtor Christoph Strobl - * @auhtor Mark Paluch + * @author Christoph Strobl + * @author Mark Paluch */ public abstract class DecodeUtils { diff --git a/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java b/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java index fb16b9f66a..00637153eb 100644 --- a/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java +++ b/src/main/java/org/springframework/data/redis/connection/zset/DefaultTuple.java @@ -36,7 +36,7 @@ public class DefaultTuple implements Tuple { /** * Constructs a new {@link DefaultTuple}. * - * @param value {@link byte[]} of the member's raw value. + * @param value {@code byte[]} of the member's raw value. * @param score {@link Double score} of the raw value used in sorting. */ public DefaultTuple(byte[] value, Double score) { diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java index f951a87287..a3a69bbd0f 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java @@ -361,8 +361,8 @@ default Mono getLast(K key) { * * @param key must not be {@literal null}. * @param timeout maximal duration to wait until an entry in the list at {@code key} is available. Must be either - * {@link Duration#ZERO} or greater {@link 1 second}, must not be {@literal null}. A timeout of zero can be - * used to wait indefinitely. Durations between zero and one second are not supported. + * {@link Duration#ZERO} or greater {@literal 1 second}, must not be {@literal null}. A timeout of zero can + * be used to wait indefinitely. Durations between zero and one second are not supported. * @return * @see Redis Documentation: BLPOP */ @@ -394,8 +394,8 @@ default Mono getLast(K key) { * * @param key must not be {@literal null}. * @param timeout maximal duration to wait until an entry in the list at {@code key} is available. Must be either - * {@link Duration#ZERO} or greater {@link 1 second}, must not be {@literal null}. A timeout of zero can be - * used to wait indefinitely. Durations between zero and one second are not supported. + * {@link Duration#ZERO} or greater {@literal 1 second}, must not be {@literal null}. A timeout of zero can + * be used to wait indefinitely. Durations between zero and one second are not supported. * @return * @see Redis Documentation: BRPOP */ @@ -418,8 +418,8 @@ default Mono getLast(K key) { * @param sourceKey must not be {@literal null}. * @param destinationKey must not be {@literal null}. * @param timeout maximal duration to wait until an entry in the list at {@code sourceKey} is available. Must be - * either {@link Duration#ZERO} or greater {@link 1 second}, must not be {@literal null}. A timeout of zero - * can be used to wait indefinitely. Durations between zero and one second are not supported. + * either {@link Duration#ZERO} or greater {@literal 1 second}, must not be {@literal null}. A timeout of + * zero can be used to wait indefinitely. Durations between zero and one second are not supported. * @return * @see Redis Documentation: BRPOPLPUSH */ diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java index eb7e882ed8..e98bfd5929 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java @@ -507,8 +507,8 @@ default Flux> scan(K key) { * * @param key must not be {@literal null}. * @param timeout maximal duration to wait until an entry in the list at {@code key} is available. Must be either - * {@link Duration#ZERO} or greater {@link 1 second}, must not be {@literal null}. A timeout of zero can be - * used to wait indefinitely. Durations between zero and one second are not supported. + * {@link Duration#ZERO} or greater {@literal 1 second}, must not be {@literal null}. A timeout of zero can + * be used to wait indefinitely. Durations between zero and one second are not supported. * @return * @see Redis Documentation: ZPOPMIN * @since 2.6 @@ -541,8 +541,8 @@ default Flux> scan(K key) { * * @param key must not be {@literal null}. * @param timeout maximal duration to wait until an entry in the list at {@code key} is available. Must be either - * {@link Duration#ZERO} or greater {@link 1 second}, must not be {@literal null}. A timeout of zero can be - * used to wait indefinitely. Durations between zero and one second are not supported. + * {@link Duration#ZERO} or greater {@literal 1 second}, must not be {@literal null}. A timeout of zero can + * be used to wait indefinitely. Durations between zero and one second are not supported. * @return * @see Redis Documentation: ZPOPMIN * @since 2.6 diff --git a/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java b/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java index 8ae2902193..3e8193a15e 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java +++ b/src/main/java/org/springframework/data/redis/core/RedisConnectionUtils.java @@ -646,7 +646,7 @@ public interface RedisConnectionProxy extends RedisConnection, RawTargetAccess { *

      * This will typically be the native driver {@link RedisConnection} or a wrapper from a connection pool. * - * @return the underlying {@link RedisConnection} (never {@link null}). + * @return the underlying {@link RedisConnection} (never {@literal null}). */ RedisConnection getTargetConnection(); diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java index ea74c8fc5c..3d527becd7 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java @@ -571,11 +571,11 @@ public void clear() { } /** - * Creates a new {@link byte[] key} using the given {@link String keyspace} and {@link String id}. + * Creates a new {@code byte[] key} using the given {@link String keyspace} and {@link String id}. * * @param keyspace {@link String name} of the Redis {@literal keyspace}. * @param id {@link String} identifying the key. - * @return a {@link byte[]} constructed from the {@link String keyspace} and {@link String id}. + * @return a {@code byte[]} constructed from the {@link String keyspace} and {@link String id}. */ public byte[] createKey(String keyspace, String id) { return toBytes(keyspace + ":" + id); diff --git a/src/main/java/org/springframework/data/redis/core/convert/Bucket.java b/src/main/java/org/springframework/data/redis/core/convert/Bucket.java index e832acf8e7..5b832b2d62 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/Bucket.java +++ b/src/main/java/org/springframework/data/redis/core/convert/Bucket.java @@ -178,7 +178,7 @@ public Bucket extract(String prefix) { * Get all the keys matching a given path. * * @param path the path to look for. Can be {@literal null}. - * @return all keys if path is {@null} or empty. + * @return all keys if path is {@literal null} or empty. */ public Set extractAllKeysFor(String path) { diff --git a/src/main/java/org/springframework/data/redis/serializer/RedisSerializationContext.java b/src/main/java/org/springframework/data/redis/serializer/RedisSerializationContext.java index 42f9c75639..9136d05cdc 100644 --- a/src/main/java/org/springframework/data/redis/serializer/RedisSerializationContext.java +++ b/src/main/java/org/springframework/data/redis/serializer/RedisSerializationContext.java @@ -242,7 +242,7 @@ static SerializationPair raw() { } /** - * Creates a pass through {@link SerializationPair} to pass-thru {@link byte} objects. + * Creates a pass through {@link SerializationPair} to pass-thru {@code byte} objects. * * @return a pass through {@link SerializationPair}. * @since 2.2 diff --git a/src/main/java/org/springframework/data/redis/serializer/RedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/RedisSerializer.java index 5141c5e388..709bd4c794 100644 --- a/src/main/java/org/springframework/data/redis/serializer/RedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/RedisSerializer.java @@ -65,7 +65,7 @@ static RedisSerializer json() { } /** - * Obtain a simple {@link java.lang.String} to {@literal byte[]} (and back) serializer using + * Obtain a simple {@link java.lang.String} to {@code byte[]} (and back) serializer using * {@link java.nio.charset.StandardCharsets#UTF_8 UTF-8} as the default {@link java.nio.charset.Charset}. * * @return never {@literal null}. diff --git a/src/main/java/org/springframework/data/redis/serializer/RedisSerializerToSerializationPairAdapter.java b/src/main/java/org/springframework/data/redis/serializer/RedisSerializerToSerializationPairAdapter.java index fc436d8dd5..f7f5b72fcc 100644 --- a/src/main/java/org/springframework/data/redis/serializer/RedisSerializerToSerializationPairAdapter.java +++ b/src/main/java/org/springframework/data/redis/serializer/RedisSerializerToSerializationPairAdapter.java @@ -55,7 +55,7 @@ static SerializationPair raw() { } /** - * @return the {@link RedisSerializerToSerializationPairAdapter} for {@link byte[]}. + * @return the {@link RedisSerializerToSerializationPairAdapter} for {@code byte[]}. * @since 2.2 */ static SerializationPair byteArray() { diff --git a/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java index b9fea3cb27..1a0abd14bd 100644 --- a/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/StringRedisSerializer.java @@ -22,7 +22,7 @@ import org.springframework.util.Assert; /** - * Simple {@link java.lang.String} to {@literal byte[]} (and back) serializer. Converts {@link java.lang.String Strings} + * Simple {@link java.lang.String} to {@code byte[]} (and back) serializer. Converts {@link java.lang.String Strings} * into bytes and vice-versa using the specified charset (by default {@literal UTF-8}). *

      * Useful when the interaction with the Redis happens mainly through Strings. diff --git a/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java b/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java index 851a1f6d66..b54a0ce67b 100644 --- a/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java +++ b/src/main/java/org/springframework/data/redis/stream/StreamMessageListenerContainer.java @@ -94,7 +94,7 @@ *

        * RedisConnectionFactory factory = …;
        *
      - * StreamMessageListenerContainer> container = StreamMessageListenerContainer.create(factory);
      + * StreamMessageListenerContainer<String, MapRecord<String, String, String>> container = StreamMessageListenerContainer.create(factory);
        * Subscription subscription = container.receive(StreamOffset.fromStart("my-stream"), message -> …);
        *
        * container.start();
      diff --git a/src/main/java/org/springframework/data/redis/stream/StreamReceiver.java b/src/main/java/org/springframework/data/redis/stream/StreamReceiver.java
      index 89cccd0ad8..96f9591c8f 100644
      --- a/src/main/java/org/springframework/data/redis/stream/StreamReceiver.java
      +++ b/src/main/java/org/springframework/data/redis/stream/StreamReceiver.java
      @@ -90,10 +90,10 @@
        * 
        * ReactiveRedisConnectionFactory factory = …;
        *
      - * StreamReceiver receiver = StreamReceiver.create(factory);
      - * Flux> records = receiver.receive(StreamOffset.fromStart("my-stream"));
      + * StreamReceiver<String, String, String> receiver = StreamReceiver.create(factory);
      + * Flux<MapRecord<String, String, String>> records = receiver.receive(StreamOffset.fromStart("my-stream"));
        *
      - * recordFlux.doOnNext(record -> …);
      + * recordFlux.doOnNext(record -> …);
        * 
      * * @author Mark Paluch diff --git a/src/main/java/org/springframework/data/redis/support/collections/package-info.java b/src/main/java/org/springframework/data/redis/support/collections/package-info.java index 35440023c1..9e45b4bdb3 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/package-info.java +++ b/src/main/java/org/springframework/data/redis/support/collections/package-info.java @@ -7,7 +7,7 @@ * For collections without duplicates the obvious candidate is * {@link org.springframework.data.redis.support.collections.RedisSet}. Use * {@link org.springframework.data.redis.support.collections.RedisZSet} if a certain order is required. - *

      + *

      * Lastly, for key/value associations {@link org.springframework.data.redis.support.collections.RedisMap} providing a * Map-like abstraction on top of a Redis hash. */ From 488e0eafd4b88e3a98d26ebf03099ace37d8e25c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 15 May 2025 15:12:38 +0200 Subject: [PATCH 166/187] Upgrade to Lettuce 6.6. Closes #3152 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9b13e4e4a3..ea4f4707e8 100644 --- a/pom.xml +++ b/pom.xml @@ -23,10 +23,10 @@ 1.9.4 1.4.21 2.11.1 - 6.5.5.RELEASE + 6.6.0.RELEASE 5.2.0 1.01 - 4.1.119.Final + 4.1.121.Final spring.data.redis From 4e1a35c806934f42b54933062dcf797cd71e73fe Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 15 May 2025 15:21:37 +0200 Subject: [PATCH 167/187] Upgrade to Jedis 6. Closes #3144 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea4f4707e8..2b10a25798 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 1.4.21 2.11.1 6.6.0.RELEASE - 5.2.0 + 6.0.0 1.01 4.1.121.Final spring.data.redis From 0cb9fbff291ff862b6d66eb07cb2717b2035a8de Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 16 May 2025 11:28:10 +0200 Subject: [PATCH 168/187] Prepare 3.5 GA (2025.0.0). See #3136 --- pom.xml | 22 +++++----------------- src/main/resources/notice.txt | 3 ++- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 2b10a25798..2869441e1a 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.5.0-SNAPSHOT + 3.5.0 - 3.5.0-SNAPSHOT - 3.5.0-SNAPSHOT + 3.5.0 + 3.5.0 1.9.4 1.4.21 2.11.1 @@ -388,19 +388,7 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - - - spring-milestone - https://repo.spring.io/milestone - + + diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 42ea89ab38..0ef0648dea 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 3.5 RC1 (2025.0.0) +Spring Data Redis 3.5 GA (2025.0.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -59,5 +59,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From 33d5bb16d2b1a4ba839a38c614c06f8759be1ec7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 16 May 2025 11:28:31 +0200 Subject: [PATCH 169/187] Release version 3.5 GA (2025.0.0). See #3136 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2869441e1a..8c967f3d27 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.5.0-SNAPSHOT + 3.5.0 Spring Data Redis Spring Data module for Redis From c564bcc0c394fd7bd8155cfa3d87ac7dc8295178 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 16 May 2025 11:31:15 +0200 Subject: [PATCH 170/187] Prepare next development iteration. See #3136 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8c967f3d27..b1dd86595e 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.5.0 + 3.6.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From 5f127ed53cd9862e82106c0fa611a6f9a013c979 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 16 May 2025 11:31:16 +0200 Subject: [PATCH 171/187] After release cleanups. See #3136 --- pom.xml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index b1dd86595e..b780d1e99c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 3.6.0-SNAPSHOT + 4.0.0-SNAPSHOT Spring Data Redis Spring Data module for Redis @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 3.5.0 + 4.0.0-SNAPSHOT - 3.5.0 - 3.5.0 + 4.0.0-SNAPSHOT + 4.0.0-SNAPSHOT 1.9.4 1.4.21 2.11.1 @@ -388,7 +388,19 @@ - - + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + + + spring-milestone + https://repo.spring.io/milestone + From 46c59b4d0de8636e2b510907a106196179f7405c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 19 Nov 2024 14:44:40 +0100 Subject: [PATCH 172/187] Adopt to deprecation removals in Commons. Closes #3051 --- .../repository/query/RedisPartTreeQuery.java | 6 +-- ...sitoryConfigurationExtensionUnitTests.java | 39 ++++++++++--------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/repository/query/RedisPartTreeQuery.java b/src/main/java/org/springframework/data/redis/repository/query/RedisPartTreeQuery.java index 5582f36cc4..4b69f8f23d 100644 --- a/src/main/java/org/springframework/data/redis/repository/query/RedisPartTreeQuery.java +++ b/src/main/java/org/springframework/data/redis/repository/query/RedisPartTreeQuery.java @@ -35,9 +35,9 @@ import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; +import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.util.ReflectionUtils; import org.springframework.data.util.Streamable; @@ -54,9 +54,9 @@ public class RedisPartTreeQuery extends KeyValuePartTreeQuery { private final RedisKeyValueAdapter adapter; - public RedisPartTreeQuery(QueryMethod queryMethod, QueryMethodEvaluationContextProvider evaluationContextProvider, + public RedisPartTreeQuery(QueryMethod queryMethod, ValueExpressionDelegate valueExpressionDelegate, KeyValueOperations template, Class> queryCreator) { - super(queryMethod, evaluationContextProvider, template, queryCreator); + super(queryMethod, valueExpressionDelegate, template, queryCreator); this.adapter = (RedisKeyValueAdapter) template.getKeyValueAdapter(); } diff --git a/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtensionUnitTests.java b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtensionUnitTests.java index b515de2ac4..afc66411de 100644 --- a/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtensionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtensionUnitTests.java @@ -28,6 +28,7 @@ import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.data.annotation.Id; import org.springframework.data.keyvalue.repository.KeyValueRepository; @@ -46,12 +47,12 @@ */ class RedisRepositoryConfigurationExtensionUnitTests { - private StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(Config.class, true); + private AnnotationMetadata metadata = AnnotationMetadata.introspect(Config.class); private ResourceLoader loader = new PathMatchingResourcePatternResolver(); private Environment environment = new StandardEnvironment(); private BeanDefinitionRegistry registry = new DefaultListableBeanFactory(); private RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata, - EnableRedisRepositories.class, loader, environment, registry); + EnableRedisRepositories.class, loader, environment, registry, null); private RedisRepositoryConfigurationExtension extension = new RedisRepositoryConfigurationExtension(); @@ -75,46 +76,46 @@ void isNotStrictMatchIfDomainTypeIsNotAnnotatedWithDocument() { @Test // DATAREDIS-491 void picksUpEnableKeyspaceEventsOnStartupCorrectly() { - metadata = new StandardAnnotationMetadata(Config.class, true); - BeanDefinitionRegistry beanDefintionRegistry = getBeanDefinitionRegistry(); + metadata = AnnotationMetadata.introspect(Config.class); + BeanDefinitionRegistry bdr = getBeanDefinitionRegistry(); - assertThat(getEnableKeyspaceEvents(beanDefintionRegistry)).isEqualTo((Object) EnableKeyspaceEvents.ON_STARTUP); + assertThat(getEnableKeyspaceEvents(bdr)).isEqualTo((Object) EnableKeyspaceEvents.ON_STARTUP); } @Test // DATAREDIS-491 void picksUpEnableKeyspaceEventsDefaultCorrectly() { - metadata = new StandardAnnotationMetadata(ConfigWithKeyspaceEventsDisabled.class, true); - BeanDefinitionRegistry beanDefintionRegistry = getBeanDefinitionRegistry(); + metadata = AnnotationMetadata.introspect(ConfigWithKeyspaceEventsDisabled.class); + BeanDefinitionRegistry bdr = getBeanDefinitionRegistry(); - assertThat(getEnableKeyspaceEvents(beanDefintionRegistry)).isEqualTo((Object) EnableKeyspaceEvents.OFF); + assertThat(getEnableKeyspaceEvents(bdr)).isEqualTo((Object) EnableKeyspaceEvents.OFF); } @Test // DATAREDIS-505 void picksUpDefaultKeyspaceNotificationsConfigParameterCorrectly() { metadata = new StandardAnnotationMetadata(Config.class, true); - BeanDefinitionRegistry beanDefintionRegistry = getBeanDefinitionRegistry(); + BeanDefinitionRegistry bdr = getBeanDefinitionRegistry(); - assertThat(getKeyspaceNotificationsConfigParameter(beanDefintionRegistry)).isEqualTo((Object) "Ex"); + assertThat(getKeyspaceNotificationsConfigParameter(bdr)).isEqualTo((Object) "Ex"); } @Test // DATAREDIS-505 void picksUpCustomKeyspaceNotificationsConfigParameterCorrectly() { metadata = new StandardAnnotationMetadata(ConfigWithKeyspaceEventsEnabledAndCustomEventConfig.class, true); - BeanDefinitionRegistry beanDefintionRegistry = getBeanDefinitionRegistry(); + BeanDefinitionRegistry bdr = getBeanDefinitionRegistry(); - assertThat(getKeyspaceNotificationsConfigParameter(beanDefintionRegistry)).isEqualTo((Object) "KEA"); + assertThat(getKeyspaceNotificationsConfigParameter(bdr)).isEqualTo((Object) "KEA"); } @Test // DATAREDIS-1049 void explicitlyEmptyKeyspaceNotificationsConfigParameterShouldBeCapturedCorrectly() { metadata = new StandardAnnotationMetadata(ConfigWithEmptyConfigParameter.class, true); - BeanDefinitionRegistry beanDefintionRegistry = getBeanDefinitionRegistry(); + BeanDefinitionRegistry bdr = getBeanDefinitionRegistry(); - assertThat(getKeyspaceNotificationsConfigParameter(beanDefintionRegistry)).isEqualTo(""); + assertThat(getKeyspaceNotificationsConfigParameter(bdr)).isEqualTo(""); } private static void assertDoesNotHaveRepo(Class repositoryInterface, @@ -147,7 +148,7 @@ private BeanDefinitionRegistry getBeanDefinitionRegistry() { BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry(); RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata, - EnableRedisRepositories.class, loader, environment, registry); + EnableRedisRepositories.class, loader, environment, registry, null); RedisRepositoryConfigurationExtension extension = new RedisRepositoryConfigurationExtension(); @@ -156,13 +157,13 @@ private BeanDefinitionRegistry getBeanDefinitionRegistry() { return registry; } - private Object getEnableKeyspaceEvents(BeanDefinitionRegistry beanDefintionRegistry) { - return beanDefintionRegistry.getBeanDefinition("redisKeyValueAdapter").getPropertyValues() + private Object getEnableKeyspaceEvents(BeanDefinitionRegistry bdr) { + return bdr.getBeanDefinition("redisKeyValueAdapter").getPropertyValues() .getPropertyValue("enableKeyspaceEvents").getValue(); } - private Object getKeyspaceNotificationsConfigParameter(BeanDefinitionRegistry beanDefintionRegistry) { - return beanDefintionRegistry.getBeanDefinition("redisKeyValueAdapter").getPropertyValues() + private Object getKeyspaceNotificationsConfigParameter(BeanDefinitionRegistry bdr) { + return bdr.getBeanDefinition("redisKeyValueAdapter").getPropertyValues() .getPropertyValue("keyspaceNotificationsConfigParameter").getValue(); } From a69a02e3192e8998a78b4168c2ff5adc99968d99 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 16 Jan 2025 11:50:08 +0100 Subject: [PATCH 173/187] Remove `MicrometerTracingAdapter` in favor of Lettuce's Micrometer support. Closes #3093 --- .../modules/ROOT/pages/observability.adoc | 80 +---- .../DefaultLettuceObservationConvention.java | 84 ----- .../LettuceObservationContext.java | 75 ---- .../LettuceObservationConvention.java | 37 -- .../MicrometerTracingAdapter.java | 340 ------------------ .../observability/RedisObservation.java | 175 --------- .../observability/SocketAddressEndpoint.java | 40 --- .../lettuce/observability/package-info.java | 6 - 8 files changed, 5 insertions(+), 832 deletions(-) delete mode 100644 src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java delete mode 100644 src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java delete mode 100644 src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java delete mode 100644 src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java delete mode 100644 src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java delete mode 100644 src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java delete mode 100644 src/main/java/org/springframework/data/redis/connection/lettuce/observability/package-info.java diff --git a/src/main/antora/modules/ROOT/pages/observability.adoc b/src/main/antora/modules/ROOT/pages/observability.adoc index e3a43ae122..a663d514fa 100644 --- a/src/main/antora/modules/ROOT/pages/observability.adoc +++ b/src/main/antora/modules/ROOT/pages/observability.adoc @@ -2,7 +2,7 @@ = Observability Getting insights from an application component about its operations, timing and relation to application code is crucial to understand latency. -Spring Data Redis ships with a Micrometer integration through the Lettuce driver to collect observations during Redis interaction. +Lettuce ships with a Micrometer integration to collect observations during Redis interaction. Once the integration is set up, Micrometer will create meters and spans (for distributed tracing) for each Redis command. To enable the integration, apply the following configuration to `LettuceClientConfiguration`: @@ -16,7 +16,7 @@ class ObservabilityConfiguration { public ClientResources clientResources(ObservationRegistry observationRegistry) { return ClientResources.builder() - .tracing(new MicrometerTracingAdapter(observationRegistry, "my-redis-cache")) + .tracing(new MicrometerTracing(observationRegistry, "my-redis-cache")) .build(); } @@ -31,77 +31,7 @@ class ObservabilityConfiguration { } ---- -See also https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/database/#redis[OpenTelemetry Semantic Conventions] for further reference. +See also for further reference: +* https://redis.github.io/lettuce/advanced-usage/#micrometer[Lettuce Tracing] +* https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/database/#redis[OpenTelemetry Semantic Conventions] . -[[observability-metrics]] -== Observability - Metrics - -Below you can find a list of all metrics declared by this project. - -[[observability-metrics-redis-command-observation]] -== Redis Command Observation - -____ -Timer created around a Redis command execution. -____ - -**Metric name** `spring.data.redis`. **Type** `timer` and **base unit** `seconds`. - -Fully qualified name of the enclosing class `org.springframework.data.redis.connection.lettuce.observability.RedisObservation`. - - - -.Low cardinality Keys -[cols="a,a"] -|=== -|Name | Description -|`db.operation`|Redis command value. -|`db.redis.database_index`|Redis database index. -|`db.system`|Database system. -|`db.user`|Redis user. -|`net.peer.name`|Name of the database host. -|`net.peer.port`|Logical remote port number. -|`net.sock.peer.addr`|Mongo peer address. -|`net.sock.peer.port`|Mongo peer port. -|`net.transport`|Network transport. -|=== - -.High cardinality Keys -[cols="a,a"] -|=== -|Name | Description -|`db.statement`|Redis statement. -|`spring.data.redis.command.error`|Redis error response. -|=== - -[[observability-spans]] -== Observability - Spans - -Below you can find a list of all spans declared by this project. - -[[observability-spans-redis-command-observation]] -== Redis Command Observation Span - -> Timer created around a Redis command execution. - -**Span name** `spring.data.redis`. - -Fully qualified name of the enclosing class `org.springframework.data.redis.connection.lettuce.observability.RedisObservation`. - - - -.Tag Keys -|=== -|Name | Description -|`db.operation`|Redis command value. -|`db.redis.database_index`|Redis database index. -|`db.statement`|Redis statement. -|`db.system`|Database system. -|`db.user`|Redis user. -|`net.peer.name`|Name of the database host. -|`net.peer.port`|Logical remote port number. -|`net.sock.peer.addr`|Mongo peer address. -|`net.sock.peer.port`|Mongo peer port. -|`net.transport`|Network transport. -|`spring.data.redis.command.error`|Redis error response. -|=== diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java deleted file mode 100644 index ca4fdc65e5..0000000000 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/DefaultLettuceObservationConvention.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection.lettuce.observability; - -import io.lettuce.core.protocol.RedisCommand; -import io.lettuce.core.tracing.Tracing.Endpoint; -import io.micrometer.common.KeyValues; - -import java.net.InetSocketAddress; -import java.util.Locale; - -import org.springframework.data.redis.connection.lettuce.observability.RedisObservation.HighCardinalityCommandKeyNames; -import org.springframework.data.redis.connection.lettuce.observability.RedisObservation.LowCardinalityCommandKeyNames; - -/** - * Default {@link LettuceObservationConvention} implementation. - * - * @author Mark Paluch - * @since 3.0 - * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through - * {@link io.lettuce.core.tracing.MicrometerTracing}. - */ -@Deprecated(since = "3.4", forRemoval = true) -record DefaultLettuceObservationConvention( - boolean includeCommandArgsInSpanTags) implements LettuceObservationConvention { - - @Override - public KeyValues getLowCardinalityKeyValues(LettuceObservationContext context) { - - Endpoint ep = context.getRequiredEndpoint(); - KeyValues keyValues = KeyValues.of(LowCardinalityCommandKeyNames.DATABASE_SYSTEM.withValue("redis"), // - LowCardinalityCommandKeyNames.REDIS_COMMAND.withValue(context.getRequiredCommand().getType().toString())); - - if (ep instanceof SocketAddressEndpoint endpoint) { - - if (endpoint.socketAddress() instanceof InetSocketAddress inet) { - keyValues = keyValues - .and(KeyValues.of(LowCardinalityCommandKeyNames.NET_SOCK_PEER_ADDR.withValue(inet.getHostString()), - LowCardinalityCommandKeyNames.NET_SOCK_PEER_PORT.withValue("" + inet.getPort()), - LowCardinalityCommandKeyNames.NET_TRANSPORT.withValue("IP.TCP"))); - } else { - keyValues = keyValues - .and(KeyValues.of(LowCardinalityCommandKeyNames.NET_PEER_NAME.withValue(endpoint.toString()), - LowCardinalityCommandKeyNames.NET_TRANSPORT.withValue("Unix"))); - } - } - - return keyValues; - } - - @Override - public KeyValues getHighCardinalityKeyValues(LettuceObservationContext context) { - - RedisCommand command = context.getRequiredCommand(); - - if (includeCommandArgsInSpanTags) { - - if (command.getArgs() != null) { - return KeyValues.of(HighCardinalityCommandKeyNames.STATEMENT - .withValue(command.getType().toString() + " " + command.getArgs().toCommandString())); - } - } - - return KeyValues.empty(); - } - - @Override - public String getContextualName(LettuceObservationContext context) { - return context.getRequiredCommand().getType().toString().toLowerCase(Locale.ROOT); - } -} diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java deleted file mode 100644 index eb1321f511..0000000000 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationContext.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection.lettuce.observability; - -import io.lettuce.core.protocol.RedisCommand; -import io.lettuce.core.tracing.Tracing.Endpoint; -import io.micrometer.observation.Observation; -import io.micrometer.observation.transport.Kind; -import io.micrometer.observation.transport.SenderContext; - -import org.springframework.lang.Nullable; - -/** - * Micrometer {@link Observation.Context} holding Lettuce contextual details. - * - * @author Mark Paluch - * @since 3.0 - * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through - * {@link io.lettuce.core.tracing.MicrometerTracing}. - */ -@Deprecated(since = "3.4", forRemoval = true) -public class LettuceObservationContext extends SenderContext { - - private volatile @Nullable RedisCommand command; - - private volatile @Nullable Endpoint endpoint; - - public LettuceObservationContext(String serviceName) { - super((carrier, key, value) -> {}, Kind.CLIENT); - setRemoteServiceName(serviceName); - } - - public RedisCommand getRequiredCommand() { - - RedisCommand local = command; - - if (local == null) { - throw new IllegalArgumentException("LettuceObservationContext is not associated with a Command"); - } - - return local; - } - - public void setCommand(RedisCommand command) { - this.command = command; - } - - public Endpoint getRequiredEndpoint() { - - Endpoint local = endpoint; - - if (local == null) { - throw new IllegalArgumentException("LettuceObservationContext is not associated with a Endpoint"); - } - - return local; - } - - public void setEndpoint(Endpoint endpoint) { - this.endpoint = endpoint; - } -} diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java deleted file mode 100644 index ea01647152..0000000000 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/LettuceObservationConvention.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection.lettuce.observability; - -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationConvention; - -/** - * {@link ObservationConvention} for {@link LettuceObservationContext}. - * - * @author Mark Paluch - * @since 3.0 - * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through - * {@link io.lettuce.core.tracing.MicrometerTracing}. - */ -@Deprecated(since = "3.4", forRemoval = true) -interface LettuceObservationConvention extends ObservationConvention { - - @Override - default boolean supportsContext(Observation.Context context) { - return context instanceof LettuceObservationContext; - } - -} diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java deleted file mode 100644 index bbf3d28fe6..0000000000 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/MicrometerTracingAdapter.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection.lettuce.observability; - -import io.lettuce.core.protocol.CompleteableCommand; -import io.lettuce.core.protocol.RedisCommand; -import io.lettuce.core.tracing.TraceContext; -import io.lettuce.core.tracing.TraceContextProvider; -import io.lettuce.core.tracing.Tracer; -import io.lettuce.core.tracing.Tracer.Span; -import io.lettuce.core.tracing.TracerProvider; -import io.lettuce.core.tracing.Tracing; -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationRegistry; -import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor; -import reactor.core.publisher.Mono; - -import java.net.SocketAddress; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.data.redis.connection.lettuce.observability.RedisObservation.HighCardinalityCommandKeyNames; -import org.springframework.lang.Nullable; - -/** - * {@link Tracing} adapter using Micrometer's {@link Observation}. This adapter integrates with Micrometer to propagate - * observations into timers, distributed traces and any other registered handlers. Observations include a set of tags - * capturing Redis runtime information. - *

      Capturing full statements

      This adapter can capture full statements when enabling - * {@code includeCommandArgsInSpanTags}. You should carefully consider the impact of this setting as all command - * arguments will be captured in traces including these that may contain sensitive details. - * - * @author Mark Paluch - * @author Yanming Zhou - * @since 3.0 - * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through - * {@link io.lettuce.core.tracing.MicrometerTracing}. - */ -@Deprecated(since = "3.4", forRemoval = true) -public class MicrometerTracingAdapter implements Tracing { - - private static final Log log = LogFactory.getLog(MicrometerTracingAdapter.class); - - private final ObservationRegistry observationRegistry; - private final String serviceName; - private final boolean includeCommandArgsInSpanTags; - - private final LettuceObservationConvention observationConvention; - - /** - * Create a new {@link MicrometerTracingAdapter} instance. - * - * @param observationRegistry must not be {@literal null}. - * @param serviceName service name to be used. - */ - public MicrometerTracingAdapter(ObservationRegistry observationRegistry, String serviceName) { - this(observationRegistry, serviceName, false); - } - - /** - * Create a new {@link MicrometerTracingAdapter} instance. - * - * @param observationRegistry must not be {@literal null}. - * @param serviceName service name to be used. - * @param includeCommandArgsInSpanTags whether to attach the full command into the trace. Use this flag with caution - * as sensitive arguments will be captured in the observation spans and metric tags. - */ - public MicrometerTracingAdapter(ObservationRegistry observationRegistry, String serviceName, - boolean includeCommandArgsInSpanTags) { - - this.observationRegistry = observationRegistry; - this.serviceName = serviceName; - this.observationConvention = new DefaultLettuceObservationConvention(includeCommandArgsInSpanTags); - this.includeCommandArgsInSpanTags = includeCommandArgsInSpanTags; - } - - @Override - public TracerProvider getTracerProvider() { - return () -> new MicrometerTracer(observationRegistry); - } - - @Override - public TraceContextProvider initialTraceContextProvider() { - return new MicrometerTraceContextProvider(observationRegistry); - } - - @Override - public boolean isEnabled() { - return true; - } - - @Override - public boolean includeCommandArgsInSpanTags() { - return includeCommandArgsInSpanTags; - } - - @Override - public Endpoint createEndpoint(SocketAddress socketAddress) { - return new SocketAddressEndpoint(socketAddress); - } - - /** - * {@link Tracer} implementation based on Micrometer's {@link ObservationRegistry}. - */ - public class MicrometerTracer extends Tracer { - - private final ObservationRegistry observationRegistry; - - public MicrometerTracer(ObservationRegistry observationRegistry) { - this.observationRegistry = observationRegistry; - } - - @Override - public Tracer.Span nextSpan() { - return this.postProcessSpan(createObservation(null)); - } - - @Override - public Tracer.Span nextSpan(TraceContext traceContext) { - return postProcessSpan(createObservation(traceContext)); - } - - private Observation createObservation(@Nullable TraceContext parentContext) { - - return RedisObservation.REDIS_COMMAND_OBSERVATION.observation(observationRegistry, () -> { - - LettuceObservationContext context = new LettuceObservationContext(serviceName); - - if (parentContext instanceof MicrometerTraceContext traceContext) { - context.setParentObservation(traceContext.observation()); - } - return context; - }); - } - - private Tracer.Span postProcessSpan(Observation observation) { - - return !observation.isNoop() ? new MicrometerSpan(observation.observationConvention(observationConvention)) - : NoOpSpan.INSTANCE; - } - } - - /** - * No-op {@link Span} implementation. - */ - static class NoOpSpan extends Tracer.Span { - - static final NoOpSpan INSTANCE = new NoOpSpan(); - - public NoOpSpan() {} - - @Override - public Tracer.Span start(RedisCommand command) { - return this; - } - - @Override - public Tracer.Span name(String name) { - return this; - } - - @Override - public Tracer.Span annotate(String value) { - return this; - } - - @Override - public Tracer.Span tag(String key, String value) { - return this; - } - - @Override - public Tracer.Span error(Throwable throwable) { - return this; - } - - @Override - public Tracer.Span remoteEndpoint(Tracing.Endpoint endpoint) { - return this; - } - - @Override - public void finish() {} - } - - /** - * Micrometer {@link Observation}-based {@link Span} implementation. - */ - static class MicrometerSpan extends Tracer.Span { - - private final Observation observation; - - private @Nullable RedisCommand command; - - public MicrometerSpan(Observation observation) { - this.observation = observation; - } - - @Override - public Span start(RedisCommand command) { - - ((LettuceObservationContext) observation.getContext()).setCommand(command); - - this.command = command; - - if (log.isDebugEnabled()) { - log.debug("Starting Observation for Command %s".formatted(command)); - } - - if (command instanceof CompleteableCommand completeableCommand) { - - completeableCommand.onComplete((o, throwable) -> { - - if (command.getOutput() != null) { - - String error = command.getOutput().getError(); - if (error != null) { - observation.highCardinalityKeyValue(HighCardinalityCommandKeyNames.ERROR.withValue(error)); - } else if (throwable != null) { - error(throwable); - } - } - - finish(); - }); - } else { - throw new IllegalArgumentException("Command " + command - + " must implement CompleteableCommand to attach Span completion to command completion"); - } - - observation.start(); - return this; - } - - @Override - public Span name(String name) { - return this; - } - - @Override - public Span annotate(String annotation) { - return this; - } - - @Override - public Span tag(String key, String value) { - observation.highCardinalityKeyValue(key, value); - return this; - } - - @Override - public Span error(Throwable throwable) { - - if (log.isDebugEnabled()) { - log.debug("Attaching error to Observation for Command %s".formatted(command)); - } - - observation.error(throwable); - return this; - } - - @Override - public Span remoteEndpoint(Endpoint endpoint) { - - ((LettuceObservationContext) observation.getContext()).setEndpoint(endpoint); - return this; - } - - @Override - public void finish() { - - if (log.isDebugEnabled()) { - log.debug("Stopping Observation for Command %s".formatted(command)); - } - - observation.stop(); - } - } - - /** - * {@link TraceContextProvider} using {@link ObservationRegistry}. - */ - record MicrometerTraceContextProvider(ObservationRegistry registry) implements TraceContextProvider { - - @Override - @Nullable - public TraceContext getTraceContext() { - - Observation observation = registry.getCurrentObservation(); - - if (observation == null) { - return null; - } - - return new MicrometerTraceContext(observation); - } - - @Override - public Mono getTraceContextLater() { - - return Mono.deferContextual(Mono::justOrEmpty).filter((it) -> { - return it.hasKey(TraceContext.class) || it.hasKey(Observation.class) - || it.hasKey(ObservationThreadLocalAccessor.KEY); - }).map((it) -> { - - if (it.hasKey(Observation.class)) { - return new MicrometerTraceContext(it.get(Observation.class)); - } - - if (it.hasKey(TraceContext.class)) { - return it.get(TraceContext.class); - } - - return new MicrometerTraceContext(it.get(ObservationThreadLocalAccessor.KEY)); - }); - } - } - - /** - * {@link TraceContext} implementation using {@link Observation}. - * - * @param observation - */ - record MicrometerTraceContext(Observation observation) implements TraceContext { - - } -} diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java deleted file mode 100644 index 989f2cece8..0000000000 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/RedisObservation.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2013-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection.lettuce.observability; - -import io.micrometer.common.docs.KeyName; -import io.micrometer.observation.docs.ObservationDocumentation; - -/** - * A Redis-based {@link io.micrometer.observation.Observation}. - * - * @author Mark Paluch - * @since 3.0 - * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through - * {@link io.lettuce.core.tracing.MicrometerTracing}. - */ -@Deprecated(since = "3.4", forRemoval = true) -public enum RedisObservation implements ObservationDocumentation { - - /** - * Timer created around a Redis command execution. - */ - REDIS_COMMAND_OBSERVATION { - - @Override - public String getName() { - return "spring.data.redis"; - } - - @Override - public KeyName[] getLowCardinalityKeyNames() { - return LowCardinalityCommandKeyNames.values(); - } - - @Override - public KeyName[] getHighCardinalityKeyNames() { - return HighCardinalityCommandKeyNames.values(); - } - }; - - /** - * Enums related to low cardinality key names for Redis commands. - */ - enum LowCardinalityCommandKeyNames implements KeyName { - - /** - * Database system. - */ - DATABASE_SYSTEM { - @Override - public String asString() { - return "db.system"; - } - }, - - /** - * Network transport. - */ - NET_TRANSPORT { - @Override - public String asString() { - return "net.transport"; - } - }, - - /** - * Name of the database host. - */ - NET_PEER_NAME { - @Override - public String asString() { - return "net.peer.name"; - } - }, - - /** - * Logical remote port number. - */ - NET_PEER_PORT { - @Override - public String asString() { - return "net.peer.port"; - } - }, - - /** - * Mongo peer address. - */ - NET_SOCK_PEER_ADDR { - @Override - public String asString() { - return "net.sock.peer.addr"; - } - }, - - /** - * Mongo peer port. - */ - NET_SOCK_PEER_PORT { - @Override - public String asString() { - return "net.sock.peer.port"; - } - }, - - /** - * Redis user. - */ - DB_USER { - @Override - public String asString() { - return "db.user"; - } - }, - - /** - * Redis database index. - */ - DB_INDEX { - @Override - public String asString() { - return "db.redis.database_index"; - } - }, - - /** - * Redis command value. - */ - REDIS_COMMAND { - @Override - public String asString() { - return "db.operation"; - } - } - - } - - /** - * Enums related to high cardinality key names for Redis commands. - */ - enum HighCardinalityCommandKeyNames implements KeyName { - - /** - * Redis statement. - */ - STATEMENT { - @Override - public String asString() { - return "db.statement"; - } - }, - - /** - * Redis error response. - */ - ERROR { - @Override - public String asString() { - return "spring.data.redis.command.error"; - } - } - } -} diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java deleted file mode 100644 index fdaf1c27e1..0000000000 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/SocketAddressEndpoint.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.redis.connection.lettuce.observability; - -import io.lettuce.core.tracing.Tracing.Endpoint; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; - -/** - * @author Mark Paluch - * @deprecated since 3.4 for removal with the next major revision. Use Lettuce's Micrometer integration through - * {@link io.lettuce.core.tracing.MicrometerTracing}. - */ -@Deprecated(since = "3.4", forRemoval = true) -record SocketAddressEndpoint(SocketAddress socketAddress) implements Endpoint { - - @Override - public String toString() { - - if (socketAddress instanceof InetSocketAddress inet) { - return inet.getHostString() + ":" + inet.getPort(); - } - - return socketAddress.toString(); - } -} diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/package-info.java b/src/main/java/org/springframework/data/redis/connection/lettuce/observability/package-info.java deleted file mode 100644 index e3231ef4c3..0000000000 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/observability/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Integration of Micrometer Tracing for Lettuce Observability. - */ -@org.springframework.lang.NonNullApi -@org.springframework.lang.NonNullFields -package org.springframework.data.redis.connection.lettuce.observability; From 7661e4af7b6b82026e50328c7e129c02d0a08251 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 24 Jan 2025 10:48:00 +0100 Subject: [PATCH 174/187] Prepare 4.0 M1 (2025.1.0). See #3046 --- pom.xml | 22 ++++---------- src/main/resources/notice.txt | 56 +---------------------------------- 2 files changed, 6 insertions(+), 72 deletions(-) diff --git a/pom.xml b/pom.xml index b780d1e99c..be03cd914c 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 4.0.0-SNAPSHOT + 4.0.0-M1 - 4.0.0-SNAPSHOT - 4.0.0-SNAPSHOT + 4.0.0-M1 + 4.0.0-M1 1.9.4 1.4.21 2.11.1 @@ -388,19 +388,7 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - - - spring-milestone - https://repo.spring.io/milestone - + + diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 0ef0648dea..228ee7b1df 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 3.5 GA (2025.0.0) +Spring Data Redis 4.0 M1 (2025.1.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -8,57 +8,3 @@ This product may include a number of subcomponents with separate copyright notices and license terms. Your use of the source code for the these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 2589ef5ce69ff25c263ddcf6604e9f0cafee36b8 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 24 Jan 2025 10:48:55 +0100 Subject: [PATCH 175/187] Release version 4.0 M1 (2025.1.0). See #3046 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be03cd914c..9f7fac02a6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 4.0.0-SNAPSHOT + 4.0.0-M1 Spring Data Redis Spring Data module for Redis From 9ec099281c9f9418b7020bc4cb8a4f2a9a013352 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 24 Jan 2025 10:53:10 +0100 Subject: [PATCH 176/187] Prepare next development iteration. See #3046 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9f7fac02a6..be03cd914c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 4.0.0-M1 + 4.0.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From b7f88e13a9abd6ab66fd5210147cf88822f464f3 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 24 Jan 2025 10:53:12 +0100 Subject: [PATCH 177/187] After release cleanups. See #3046 --- pom.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index be03cd914c..e54d0cc960 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 4.0.0-M1 + 4.0.0-SNAPSHOT - 4.0.0-M1 - 4.0.0-M1 + 4.0.0-SNAPSHOT + 4.0.0-SNAPSHOT 1.9.4 1.4.21 2.11.1 @@ -388,7 +388,19 @@ - - + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + + + spring-milestone + https://repo.spring.io/milestone + From 95da3e6fd76dc52f15d7a1e83d1c75c23c7da76a Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Mar 2025 10:20:28 +0100 Subject: [PATCH 178/187] Adopt to changes in KeyValue. --- .../springframework/data/redis/core/RedisKeyValueAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java index 3d527becd7..4cff5b30f3 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java @@ -346,12 +346,12 @@ public T delete(Object id, String keyspace, Class type) { } @Override - public List getAllOf(String keyspace) { + public List getAllOf(String keyspace) { return getAllOf(keyspace, Object.class, -1, -1); } @Override - public Iterable getAllOf(String keyspace, Class type) { + public List getAllOf(String keyspace, Class type) { return getAllOf(keyspace, type, -1, -1); } From e26ce3897d4e216e93fc5a0626735b5d35fb30ea Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Apr 2025 14:29:21 +0200 Subject: [PATCH 179/187] Prepare 4.0 M2 (2025.1.0). See #3097 --- pom.xml | 22 +++++----------------- src/main/resources/notice.txt | 3 ++- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index e54d0cc960..0b6f9bb7ff 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 4.0.0-SNAPSHOT + 4.0.0-M2 - 4.0.0-SNAPSHOT - 4.0.0-SNAPSHOT + 4.0.0-M2 + 4.0.0-M2 1.9.4 1.4.21 2.11.1 @@ -388,19 +388,7 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - - - spring-milestone - https://repo.spring.io/milestone - + + diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 228ee7b1df..9b80042f87 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 4.0 M1 (2025.1.0) +Spring Data Redis 4.0 M2 (2025.1.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -8,3 +8,4 @@ This product may include a number of subcomponents with separate copyright notices and license terms. Your use of the source code for the these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. + From 9aff345887cbfb0b6b3d249d8c45f8b7c09727ed Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Apr 2025 14:29:39 +0200 Subject: [PATCH 180/187] Release version 4.0 M2 (2025.1.0). See #3097 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0b6f9bb7ff..bed748e7fe 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 4.0.0-SNAPSHOT + 4.0.0-M2 Spring Data Redis Spring Data module for Redis From 5fc3bab347d1d6004168da4e0aa6842314290ffa Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Apr 2025 14:32:03 +0200 Subject: [PATCH 181/187] Prepare next development iteration. See #3097 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bed748e7fe..0b6f9bb7ff 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 4.0.0-M2 + 4.0.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From 551814cde4506f621a11758411ea809c2cb90555 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Apr 2025 14:32:04 +0200 Subject: [PATCH 182/187] After release cleanups. See #3097 --- pom.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 0b6f9bb7ff..e54d0cc960 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 4.0.0-M2 + 4.0.0-SNAPSHOT - 4.0.0-M2 - 4.0.0-M2 + 4.0.0-SNAPSHOT + 4.0.0-SNAPSHOT 1.9.4 1.4.21 2.11.1 @@ -388,7 +388,19 @@ - - + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + + + spring-milestone + https://repo.spring.io/milestone + From fffe3f0b02d8b6da7291353584ec594de3e8e9e2 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 12 May 2025 08:54:46 +0200 Subject: [PATCH 183/187] Update CI Properties. See #3137 --- ci/pipeline.properties | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 8dd2295acc..cde4a8e881 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,5 +1,5 @@ # Java versions -java.main.tag=17.0.15_6-jdk-focal +java.main.tag=24.0.1_9-jdk-noble java.next.tag=24.0.1_9-jdk-noble # Docker container images - standard @@ -14,7 +14,6 @@ docker.mongodb.8.0.version=8.0.9 # Supported versions of Redis docker.redis.6.version=6.2.13 docker.redis.7.version=7.2.4 -docker.valkey.8.version=8.1.1 # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home From c9709814e9ffc9cb62f3485211eca69a945a009a Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 16 May 2025 14:15:56 +0200 Subject: [PATCH 184/187] Prepare 4.0 M3 (2025.1.0). See #3137 --- pom.xml | 22 +++++----------------- src/main/resources/notice.txt | 3 ++- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index e54d0cc960..ffc7cc36da 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 4.0.0-SNAPSHOT + 4.0.0-M3 - 4.0.0-SNAPSHOT - 4.0.0-SNAPSHOT + 4.0.0-M3 + 4.0.0-M3 1.9.4 1.4.21 2.11.1 @@ -388,19 +388,7 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - - - spring-milestone - https://repo.spring.io/milestone - + + diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 9b80042f87..48afb51a3f 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data Redis 4.0 M2 (2025.1.0) +Spring Data Redis 4.0 M3 (2025.1.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -9,3 +9,4 @@ separate copyright notices and license terms. Your use of the source code for the these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. + From fe7e9e861bcd608d00b7174a76567336c4758b3b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 16 May 2025 14:16:18 +0200 Subject: [PATCH 185/187] Release version 4.0 M3 (2025.1.0). See #3137 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ffc7cc36da..ac4579fa11 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 4.0.0-SNAPSHOT + 4.0.0-M3 Spring Data Redis Spring Data module for Redis From 29351df9f6384c7d0184a725ac3c260fcceefe88 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 16 May 2025 14:18:47 +0200 Subject: [PATCH 186/187] Prepare next development iteration. See #3137 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ac4579fa11..ffc7cc36da 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 4.0.0-M3 + 4.0.0-SNAPSHOT Spring Data Redis Spring Data module for Redis From adb23d6ae4f80b1f2d5d4696c329938c608ff639 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 16 May 2025 14:18:48 +0200 Subject: [PATCH 187/187] After release cleanups. See #3137 --- pom.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index ffc7cc36da..b780d1e99c 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,12 @@ org.springframework.data.build spring-data-parent - 4.0.0-M3 + 4.0.0-SNAPSHOT - 4.0.0-M3 - 4.0.0-M3 + 4.0.0-SNAPSHOT + 4.0.0-SNAPSHOT 1.9.4 1.4.21 2.11.1 @@ -388,7 +388,19 @@ - - + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + + + spring-milestone + https://repo.spring.io/milestone +