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 09b0719cb..72bcb1e74 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 @@ -452,7 +452,7 @@ class JedisClusterSetCommands implements RedisSetCommands { ScanParams params = JedisConverters.toScanParams(options); redis.clients.jedis.ScanResult result = connection.getCluster().sscan(key, JedisConverters.toBytes(cursorId), params); - return new ScanIteration<>(Long.valueOf(result.getCursor()), result.getResult()); + return new ScanIteration<>(Long.parseLong(result.getCursor()), result.getResult()); } }.open(); } 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 0c5374d54..969727ce9 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 @@ -169,7 +169,7 @@ class JedisKeyCommands implements RedisKeyCommands { ScanParams params = JedisConverters.toScanParams(options); redis.clients.jedis.ScanResult result = connection.getJedis().scan(Long.toString(cursorId), params); - return new ScanIteration<>(Long.valueOf(result.getCursor()), + return new ScanIteration<>(Long.parseLong(result.getCursor()), JedisConverters.stringListToByteList().convert(result.getResult())); } 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 ababeba1e..ce9bdec0d 100644 --- a/src/main/java/org/springframework/data/redis/core/ConvertingCursor.java +++ b/src/main/java/org/springframework/data/redis/core/ConvertingCursor.java @@ -15,8 +15,6 @@ */ package org.springframework.data.redis.core; -import java.io.IOException; - import org.springframework.core.convert.converter.Converter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -81,7 +79,7 @@ public class ConvertingCursor implements Cursor { * @see java.io.Closeable#close() */ @Override - public void close() throws IOException { + public void close() { delegate.close(); } 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 dc36a86cf..0ecf77749 100644 --- a/src/main/java/org/springframework/data/redis/core/Cursor.java +++ b/src/main/java/org/springframework/data/redis/core/Cursor.java @@ -15,9 +15,8 @@ */ package org.springframework.data.redis.core; -import java.io.Closeable; - import org.springframework.data.redis.util.BoundedIterator; +import org.springframework.data.util.CloseableIterator; /** * Cursor abstraction to scan over the keyspace or elements within a data structure using a variant of a {@code SCAN} @@ -28,7 +27,7 @@ import org.springframework.data.redis.util.BoundedIterator; * @param * @since 1.4 */ -public interface Cursor extends BoundedIterator, Closeable { +public interface Cursor extends BoundedIterator, CloseableIterator { /** * Get the reference cursor.
@@ -39,19 +38,19 @@ public interface Cursor extends BoundedIterator, Closeable { long getCursorId(); /** - * @return Returns true if cursor closed. + * @return {@code true} if cursor closed. */ boolean isClosed(); /** * Opens cursor and returns itself. * - * @return + * @return the opened cursor. */ Cursor open(); /** - * @return Returns the current position of the cursor. + * @return the current position of the cursor. */ long getPosition(); 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 4693bfe00..bf46058b1 100644 --- a/src/main/java/org/springframework/data/redis/core/ScanCursor.java +++ b/src/main/java/org/springframework/data/redis/core/ScanCursor.java @@ -15,7 +15,6 @@ */ package org.springframework.data.redis.core; -import java.io.IOException; import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; @@ -39,10 +38,10 @@ import org.springframework.util.CollectionUtils; */ public abstract class ScanCursor implements Cursor { - private @Nullable CursorState state; + private CursorState state; private long cursorId; - private @Nullable Iterator delegate; - private @Nullable final ScanOptions scanOptions; + private Iterator delegate; + private final ScanOptions scanOptions; private long position; private final long limit; @@ -56,7 +55,7 @@ public abstract class ScanCursor implements Cursor { /** * Crates new {@link ScanCursor} with {@code id=0}. * - * @param options + * @param options the scan options to apply. */ public ScanCursor(ScanOptions options) { this(0, options); @@ -65,7 +64,7 @@ public abstract class ScanCursor implements Cursor { /** * Crates new {@link ScanCursor} with {@link ScanOptions#NONE} * - * @param cursorId + * @param cursorId the cursor Id. */ public ScanCursor(long cursorId) { this(cursorId, ScanOptions.NONE); @@ -74,22 +73,22 @@ public abstract class ScanCursor implements Cursor { /** * Crates new {@link ScanCursor} * - * @param cursorId - * @param options Defaulted to {@link ScanOptions#NONE} if nulled. + * @param cursorId the cursor Id. + * @param options Defaulted to {@link ScanOptions#NONE} if {@code null}. */ - public ScanCursor(long cursorId, ScanOptions options) { + public ScanCursor(long cursorId, @Nullable ScanOptions options) { this.scanOptions = options != null ? options : ScanOptions.NONE; this.cursorId = cursorId; this.state = CursorState.READY; - this.delegate = Collections. emptyList().iterator(); + this.delegate = Collections.emptyIterator(); this.limit = -1; } /** * Crates a new {@link ScanCursor}. * - * @param source + * @param source source cursor. * @param limit * @since 2.5 */ @@ -144,14 +143,7 @@ public abstract class ScanCursor implements Cursor { private void processScanResult(ScanIteration result) { - if (result == null) { - - resetDelegate(); - state = CursorState.FINISHED; - return; - } - - cursorId = Long.valueOf(result.getCursorId()); + cursorId = result.getCursorId(); if (isFinished(cursorId)) { state = CursorState.FINISHED; @@ -176,7 +168,7 @@ public abstract class ScanCursor implements Cursor { } private void resetDelegate() { - delegate = Collections. emptyList().iterator(); + delegate = Collections.emptyIterator(); } /* @@ -209,11 +201,7 @@ public abstract class ScanCursor implements Cursor { return true; } - if (cursorId > 0) { - return true; - } - - return false; + return cursorId > 0; } private void assertCursorIsOpen() { @@ -266,7 +254,7 @@ public abstract class ScanCursor implements Cursor { * @see java.io.Closeable#close() */ @Override - public final void close() throws IOException { + public final void close() { try { doClose(); @@ -326,7 +314,8 @@ public abstract class ScanCursor implements Cursor { } /** - * Wrapper for a concrete {@link ScanCursor} forwarding {@link #doScan(long, ScanOptions)}. + * Wrapper for a concrete {@link ScanCursor} forwarding {@link #doScan(long, ScanOptions)}, {@link #doClose()} and + * {@link #isClosed()}. * * @param * @since 2.5 @@ -344,5 +333,15 @@ public abstract class ScanCursor implements Cursor { protected ScanIteration doScan(long cursorId, ScanOptions options) { return delegate.doScan(cursorId, options); } + + @Override + protected void doClose() { + delegate.close(); + } + + @Override + public boolean isClosed() { + return delegate.isClosed(); + } } } 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 058797f32..67a9bdf62 100644 --- a/src/test/java/org/springframework/data/redis/core/ScanCursorUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/ScanCursorUnitTests.java @@ -24,7 +24,6 @@ import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.Queue; -import java.util.Stack; import org.junit.jupiter.api.Test; @@ -237,6 +236,26 @@ class ScanCursorUnitTests { assertThat(result).hasSize(3); } + @Test // GH-1575 + void decoratedCursorShouldForwardClose() { + + LinkedList> values = new LinkedList<>(); + values.add(createIteration(1, "spring")); + values.add(createIteration(2, "data")); + values.add(createIteration(3, "redis")); + values.add(createIteration(0)); + Cursor cursor = initCursor(values); + Cursor limited = cursor.limit(1); + + assertThat(cursor.isClosed()).isFalse(); + assertThat(limited.isClosed()).isFalse(); + + limited.close(); + + assertThat(cursor.isClosed()).isTrue(); + assertThat(limited.isClosed()).isTrue(); + } + private CapturingCursorDummy initCursor(Queue> values) { CapturingCursorDummy cursor = new CapturingCursorDummy(values); cursor.open(); @@ -247,11 +266,9 @@ class ScanCursorUnitTests { return new ScanIteration<>(cursorId, values.length > 0 ? Arrays.asList(values) : Collections. emptyList()); } - private class CapturingCursorDummy extends ScanCursor { + private static class CapturingCursorDummy extends ScanCursor { - private Queue> values; - - private Stack cursors; + private final Queue> values; CapturingCursorDummy(Queue> values) { this.values = values; @@ -260,11 +277,12 @@ class ScanCursorUnitTests { @Override protected ScanIteration doScan(long cursorId, ScanOptions options) { - if (cursors == null) { - cursors = new Stack<>(); + ScanIteration iteration = this.values.poll(); + + if (iteration == null) { + iteration = new ScanIteration<>(0, Collections.emptyList()); } - this.cursors.push(cursorId); - return this.values.poll(); + return iteration; } } }