DATAREDIS-314 - Add SCAN commands to Collection types.

Add scan to RedisMap / RedisSet / RedisZSet.
Lua scritps have to be assembled in binary way in order to avoid script errors when executing.
Add missing rules/profiles to run properly in a 2.6.x redis environment.

Original pull request: #82.
This commit is contained in:
Christoph Strobl
2014-06-12 11:04:31 +02:00
committed by Thomas Darimont
parent 7b3a358752
commit 42ff3ee1e1
18 changed files with 456 additions and 51 deletions

View File

@@ -17,6 +17,9 @@ package org.springframework.data.redis.connection.lettuce;
import static com.lambdaworks.redis.protocol.CommandType.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
@@ -3105,19 +3108,51 @@ public class LettuceConnection implements RedisConnection {
throw new UnsupportedOperationException("'HSCAN' cannot be called in pipeline / transaction mode.");
}
String params = " ,'" + LettuceConverters.bytesToString().convert(key) + "', " + cursorId
+ options.toOptionString();
String script = "return redis.call('HSCAN'" + params + ")";
List<?> result = eval(script.getBytes(), ReturnType.MULTI, 0);
byte[] script = createBinaryLuaScriptForScan("HSCAN", key, cursorId, options);
List<?> result = eval(script, ReturnType.MULTI, 0);
String nextCursorId = LettuceConverters.bytesToString().convert((byte[]) result.get(0));
Map<byte[], byte[]> values = failsafeReadScanValues(result, LettuceConverters.bytesListToMapConverter());
return new ScanIteration<Entry<byte[], byte[]>>(Long.valueOf(nextCursorId), values.entrySet());
}
}.open();
}
private byte[] createBinaryLuaScriptForScan(String command, byte[] key, long cursorId, ScanOptions options) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
outputStream.write(("return redis.call('" + command + "'").getBytes("UTF-8"));
outputStream.write(", '".getBytes("UTF-8"));
writeFilteredKey(key, outputStream);
outputStream.write("', ".getBytes("UTF-8"));
outputStream.write(Long.toString(cursorId).getBytes("UTF-8"));
outputStream.write(options.toOptionString().getBytes("UTF-8"));
outputStream.write(")".getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
return outputStream.toByteArray();
}
private void writeFilteredKey(byte[] key, OutputStream stream) {
byte toBeFiltered = (byte) '\r';
for (byte b : key) {
try {
if (toBeFiltered == b) {
stream.write("\\r".getBytes());
} else {
stream.write(b);
}
} catch (IOException e) {
throw new IllegalArgumentException("Cannot convert key to suitable redis key for lua", e);
}
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisSetCommands#sScan(byte[], org.springframework.data.redis.core.ScanOptions)
@@ -3145,11 +3180,8 @@ public class LettuceConnection implements RedisConnection {
throw new UnsupportedOperationException("'SSCAN' cannot be called in pipeline / transaction mode.");
}
String params = " ,'" + LettuceConverters.bytesToString().convert(key) + "', " + cursorId
+ options.toOptionString();
String script = "return redis.call('SSCAN'" + params + ")";
List<?> result = eval(script.getBytes(), ReturnType.MULTI, 0);
byte[] script = createBinaryLuaScriptForScan("SSCAN", key, cursorId, options);
List<?> result = eval(script, ReturnType.MULTI, 0);
String nextCursorId = LettuceConverters.bytesToString().convert((byte[]) result.get(0));
List<byte[]> values = failsafeReadScanValues(result, null);
@@ -3185,11 +3217,9 @@ public class LettuceConnection implements RedisConnection {
throw new UnsupportedOperationException("'ZSCAN' cannot be called in pipeline / transaction mode.");
}
String params = " ,'" + LettuceConverters.bytesToString().convert(key) + "', " + cursorId
+ options.toOptionString();
String script = "return redis.call('ZSCAN'" + params + ")";
byte[] script = createBinaryLuaScriptForScan("ZSCAN", key, cursorId, options);
List<?> result = eval(script, ReturnType.MULTI, 0);
List<?> result = eval(script.getBytes(), ReturnType.MULTI, 0);
String nextCursorId = LettuceConverters.bytesToString().convert((byte[]) result.get(0));
List<Tuple> values = failsafeReadScanValues(result, LettuceConverters.bytesListToTupleListConverter());

View File

@@ -26,7 +26,9 @@ import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.SessionCallback;
/**
@@ -293,4 +295,22 @@ public class DefaultRedisMap<K, V> implements RedisMap<K, V> {
throw new IllegalStateException("Cannot read collection with Redis connection in pipeline/multi-exec mode");
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisMap#scan()
*/
@Override
public Cursor<java.util.Map.Entry<K, V>> scan() {
return scan(ScanOptions.NONE);
}
/**
* @since 1.4
* @param options
* @return
*/
private Cursor<java.util.Map.Entry<K, V>> scan(ScanOptions options) {
return hashOps.scan(options);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,13 +23,16 @@ import java.util.UUID;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.ScanOptions;
/**
* Default implementation for {@link RedisSet}. Note that the collection support works only with normal,
* non-pipeline/multi-exec connections as it requires a reply to be sent right away.
*
* @author Costin Leau
* @author Christoph Strobl
*/
public class DefaultRedisSet<E> extends AbstractRedisCollection<E> implements RedisSet<E> {
@@ -162,4 +165,22 @@ public class DefaultRedisSet<E> extends AbstractRedisCollection<E> implements Re
public DataType getType() {
return DataType.SET;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisSet#scan()
*/
@Override
public Cursor<E> scan() {
return scan(ScanOptions.NONE);
}
/**
* @since 1.4
* @param options
* @return
*/
public Cursor<E> scan(ScanOptions options) {
return boundSetOps.scan(options);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,9 +20,13 @@ import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.ConvertingCursor;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
/**
@@ -30,6 +34,7 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
* non-pipeline/multi-exec connections as it requires a reply to be sent right away.
*
* @author Costin Leau
* @author Christoph Strobl
*/
public class DefaultRedisZSet<E> extends AbstractRedisCollection<E> implements RedisZSet<E> {
@@ -229,4 +234,29 @@ public class DefaultRedisZSet<E> extends AbstractRedisCollection<E> implements R
public DataType getType() {
return DataType.ZSET;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#scan()
*/
@Override
public Cursor<E> scan() {
return new ConvertingCursor<TypedTuple<E>, E>(scan(ScanOptions.NONE), new Converter<TypedTuple<E>, E>() {
@Override
public E convert(TypedTuple<E> source) {
return source.getValue();
}
});
}
/**
* @since 1.4
* @param options
* @return
*/
public Cursor<TypedTuple<E>> scan(ScanOptions options) {
return boundZSetOps.scan(options);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 the original author 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,16 +15,25 @@
*/
package org.springframework.data.redis.support.collections;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
/**
* Map view of a Redis hash.
*
* @author Costin Leau
* @author Christoph Strobl
*/
public interface RedisMap<K, V> extends RedisStore, ConcurrentMap<K, V> {
Long increment(K key, long delta);
Double increment(K key, double delta);
/**
* @since 1.4
* @return
*/
Iterator<Map.Entry<K, V>> scan();
}

View File

@@ -21,8 +21,10 @@ 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;
@@ -247,4 +249,9 @@ public class RedisProperties extends Properties implements RedisMap<Object, Obje
public synchronized void storeToXML(OutputStream os, String comment) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public Iterator<java.util.Map.Entry<Object, Object>> scan() {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,14 @@
package org.springframework.data.redis.support.collections;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
/**
* Redis extension for the {@link Set} contract. Supports {@link Set} specific operations backed by Redis operations.
*
* @author Costin Leau
* @author Christoph Strobl
*/
public interface RedisSet<E> extends RedisCollection<E>, Set<E> {
@@ -48,4 +50,10 @@ public interface RedisSet<E> extends RedisCollection<E>, Set<E> {
RedisSet<E> diffAndStore(RedisSet<?> set, String destKey);
RedisSet<E> diffAndStore(Collection<? extends RedisSet<?>> sets, String destKey);
/**
* @since 1.4
* @return
*/
Iterator<E> scan();
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.redis.support.collections;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
@@ -124,4 +125,10 @@ public interface RedisZSet<E> extends RedisCollection<E>, Set<E> {
* @throws NoSuchElementException sorted set is empty.
*/
E last();
/**
* @since 1.4
* @return
*/
Iterator<E> scan();
}

View File

@@ -25,6 +25,7 @@ import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -35,6 +36,7 @@ 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.jedis.JedisConnectionFactory;
import org.springframework.data.redis.test.util.MinimumRedisVersionRule;
import org.springframework.test.annotation.IfProfileValue;
/**
@@ -48,6 +50,9 @@ import org.springframework.test.annotation.IfProfileValue;
*/
@RunWith(Parameterized.class)
public class DefaultHashOperationsTests<K, HK, HV> {
public @Rule MinimumRedisVersionRule versionRule = new MinimumRedisVersionRule();
private RedisTemplate<K, ?> redisTemplate;
private ObjectFactory<K> keyFactory;

View File

@@ -29,6 +29,7 @@ import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -36,6 +37,7 @@ import org.junit.runners.Parameterized.Parameters;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.data.redis.test.util.MinimumRedisVersionRule;
import org.springframework.test.annotation.IfProfileValue;
/**
@@ -49,6 +51,8 @@ import org.springframework.test.annotation.IfProfileValue;
@RunWith(Parameterized.class)
public class DefaultZSetOperationsTests<K, V> {
public @Rule MinimumRedisVersionRule versionRule = new MinimumRedisVersionRule();
private RedisTemplate<K, V> redisTemplate;
private ObjectFactory<K> keyFactory;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 the original author 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,23 +15,16 @@
*/
package org.springframework.data.redis.support.collections;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.springframework.data.redis.matcher.RedisTestMatchers.isEqual;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.springframework.data.redis.matcher.RedisTestMatchers.*;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -41,6 +34,7 @@ import java.util.Set;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -53,19 +47,35 @@ import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.RedisVersionUtils;
import org.springframework.data.redis.connection.ConnectionUtils;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.test.util.MinimumRedisVersionRule;
import org.springframework.data.redis.test.util.RedisClientRule;
import org.springframework.data.redis.test.util.RedisDriver;
import org.springframework.data.redis.test.util.WithRedisDriver;
import org.springframework.test.annotation.IfProfileValue;
/**
* Integration test for Redis Map.
*
* @author Costin Leau
* @author Jennifer Hickey
* @author Christoph Strobl
* @auhtor Thomas Darimont
*/
@RunWith(Parameterized.class)
public abstract class AbstractRedisMapTests<K, V> {
public @Rule RedisClientRule clientRule = new RedisClientRule() {
public RedisConnectionFactory getConnectionFactory() {
return template.getConnectionFactory();
}
};
public @Rule MinimumRedisVersionRule versionRule = new MinimumRedisVersionRule();
protected RedisMap<K, V> map;
protected ObjectFactory<K> keyFactory;
protected ObjectFactory<V> valueFactory;
@@ -478,4 +488,29 @@ public abstract class AbstractRedisMapTests<K, V> {
public void testReplaceNullValue() {
map.replace(getKey(), null);
}
/**
* @see DATAREDIS-314
*/
@Test
@IfProfileValue(name = "redisVersion", value = "2.8+")
@WithRedisDriver({ RedisDriver.JEDIS, RedisDriver.LETTUCE })
public void testScanWorksCorrectly() {
K k1 = getKey();
K k2 = getKey();
V v1 = getValue();
V v2 = getValue();
map.put(k1, v1);
map.put(k2, v2);
Iterator<Entry<K, V>> it = map.scan();
while (it.hasNext()) {
Entry<K, V> entry = it.next();
assertThat(entry.getKey(), anyOf(equalTo(k1), equalTo(k2)));
assertThat(entry.getValue(), anyOf(equalTo(v1), equalTo(v2)));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.springframework.data.redis.support.collections;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.redis.matcher.RedisTestMatchers.isEqual;
import static org.springframework.data.redis.matcher.RedisTestMatchers.*;
import java.util.ArrayList;
import java.util.Arrays;
@@ -26,20 +26,35 @@ import java.util.List;
import java.util.Set;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.collections.DefaultRedisSet;
import org.springframework.data.redis.support.collections.RedisSet;
import org.springframework.data.redis.test.util.MinimumRedisVersionRule;
import org.springframework.data.redis.test.util.RedisClientRule;
import org.springframework.data.redis.test.util.RedisDriver;
import org.springframework.data.redis.test.util.WithRedisDriver;
import org.springframework.test.annotation.IfProfileValue;
/**
* Integration test for Redis set.
*
* @author Costin Leau
* @author Christoph Strobl
* @author Thomas Darimont
*/
public abstract class AbstractRedisSetTests<T> extends AbstractRedisCollectionTests<T> {
public @Rule RedisClientRule clientRule = new RedisClientRule() {
public RedisConnectionFactory getConnectionFactory() {
return template.getConnectionFactory();
}
};
public @Rule MinimumRedisVersionRule versionRule = new MinimumRedisVersionRule();
protected RedisSet<T> set;
/**
@@ -65,7 +80,6 @@ public abstract class AbstractRedisSetTests<T> extends AbstractRedisCollectionTe
return new DefaultRedisSet<T>((BoundSetOperations<String, T>) set.getOperations().boundSetOps(key));
}
@SuppressWarnings("unchecked")
@Test
public void testDiff() {
RedisSet<T> diffSet1 = createSetFor("test:set:diff1");
@@ -87,7 +101,6 @@ public abstract class AbstractRedisSetTests<T> extends AbstractRedisCollectionTe
assertThat(diff, hasItem(t1));
}
@SuppressWarnings("unchecked")
@Test
public void testDiffAndStore() {
RedisSet<T> diffSet1 = createSetFor("test:set:diff1");
@@ -114,7 +127,6 @@ public abstract class AbstractRedisSetTests<T> extends AbstractRedisCollectionTe
assertEquals(resultName, diff.getKey());
}
@SuppressWarnings("unchecked")
@Test
public void testIntersect() {
RedisSet<T> intSet1 = createSetFor("test:set:int1");
@@ -139,7 +151,6 @@ public abstract class AbstractRedisSetTests<T> extends AbstractRedisCollectionTe
assertThat(inter, hasItem(t2));
}
@SuppressWarnings("unchecked")
public void testIntersectAndStore() {
RedisSet<T> intSet1 = createSetFor("test:set:int1");
RedisSet<T> intSet2 = createSetFor("test:set:int2");
@@ -213,7 +224,6 @@ public abstract class AbstractRedisSetTests<T> extends AbstractRedisCollectionTe
assertEquals(resultName, union.getKey());
}
@SuppressWarnings("unchecked")
@Test
public void testIterator() {
T t1 = getT();
@@ -290,4 +300,22 @@ public abstract class AbstractRedisSetTests<T> extends AbstractRedisCollectionTe
assertEquals(0, result.size());
}
/**
* @see DATAREDIS-314
*/
@SuppressWarnings("unchecked")
@IfProfileValue(name = "redisVersion", value = "2.8+")
@Test
@WithRedisDriver({ RedisDriver.JEDIS, RedisDriver.LETTUCE })
public void testScanWorksCorrectly() {
Object[] expectedArray = new Object[] { getT(), getT(), getT() };
collection.addAll((List<T>) Arrays.asList(expectedArray));
Iterator<T> it = set.scan();
while (it.hasNext()) {
assertThat(it.next(), anyOf(equalTo(expectedArray[0]), equalTo(expectedArray[1]), equalTo(expectedArray[2])));
}
}
}

View File

@@ -15,15 +15,10 @@
*/
package org.springframework.data.redis.support.collections;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeTrue;
import static org.springframework.data.redis.matcher.RedisTestMatchers.isEqual;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.springframework.data.redis.matcher.RedisTestMatchers.*;
import java.util.Arrays;
import java.util.Iterator;
@@ -31,21 +26,37 @@ import java.util.NoSuchElementException;
import java.util.Set;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.connection.ConnectionUtils;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.data.redis.test.util.MinimumRedisVersionRule;
import org.springframework.data.redis.test.util.RedisClientRule;
import org.springframework.data.redis.test.util.RedisDriver;
import org.springframework.data.redis.test.util.WithRedisDriver;
import org.springframework.test.annotation.IfProfileValue;
/**
* Integration test for Redis ZSet.
*
* @author Costin Leau
* @author Jennifer Hickey
* @author Thomas Darimont
*/
public abstract class AbstractRedisZSetTest<T> extends AbstractRedisCollectionTests<T> {
public @Rule RedisClientRule clientRule = new RedisClientRule() {
public RedisConnectionFactory getConnectionFactory() {
return template.getConnectionFactory();
}
};
public @Rule MinimumRedisVersionRule versionRule = new MinimumRedisVersionRule();
protected RedisZSet<T> zSet;
/**
@@ -519,4 +530,28 @@ public abstract class AbstractRedisZSetTest<T> extends AbstractRedisCollectionTe
Object[] array = collection.toArray(new Object[zSet.size()]);
assertArrayEquals(new Object[] { t1, t2, t3, t4 }, array);
}
/**
* @see DATAREDIS-314
*/
@IfProfileValue(name = "redisVersion", value = "2.8+")
@Test
@WithRedisDriver({ RedisDriver.JEDIS, RedisDriver.LETTUCE })
public void testScanWorksCorrectly() {
T t1 = getT();
T t2 = getT();
T t3 = getT();
T t4 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
zSet.add(t4, 4);
Iterator<T> it = zSet.scan();
while (it.hasNext()) {
assertThat(it.next(), anyOf(equalTo(t1), equalTo(t2), equalTo(t3), equalTo(t4)));
}
}
}

View File

@@ -52,6 +52,7 @@ import org.springframework.oxm.xstream.XStreamMarshaller;
/**
* @author Costin Leau
* @author Thomas Darimont
* @author Christoph Strobl
*/
public class RedisPropertiesTests extends RedisMapTests {
@@ -220,6 +221,12 @@ public class RedisPropertiesTests extends RedisMapTests {
assertTrue(keys.contains(key3));
}
@Override
@Test(expected = UnsupportedOperationException.class)
public void testScanWorksCorrectly() {
super.testScanWorksCorrectly();
}
/**
* @see DATAREDIS-241
*/
@@ -371,4 +378,5 @@ public class RedisPropertiesTests extends RedisMapTests {
{ stringFactory, stringFactory, xGenericTemplateSrp }, { stringFactory, stringFactory, jsonPersonTemplateSrp },
{ stringFactory, stringFactory, jackson2JsonPersonTemplateSrp } });
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.data.redis.RedisVersionUtils;
import org.springframework.data.redis.SettingsUtils;
import org.springframework.data.redis.Version;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.test.annotation.IfProfileValue;
import org.springframework.util.StringUtils;
@@ -45,9 +45,10 @@ public class MinimumRedisVersionRule implements TestRule {
private Version readServerVersion() {
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory();
connectionFactory.setHostName(SettingsUtils.getHost());
connectionFactory.setPort(SettingsUtils.getPort());
connectionFactory.setTimeout(100);
connectionFactory.afterPropertiesSet();
RedisConnection connection = connectionFactory.getConnection();

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2014 the original author 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
*
* http://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.test.util;
import org.junit.internal.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.springframework.data.redis.connection.RedisConnectionFactory;
/**
* A JUnit {@link TestRule} that checks whether a given {@link RedisConnectionFactory} is from the required
* {@link RedisDriver}.
*
* @author Christoph Strobl
* @author Thomas Darimont
*/
public abstract class RedisClientRule implements TestRule {
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
WithRedisDriver withRedisDriver = description.getAnnotation(WithRedisDriver.class);
RedisConnectionFactory redisConnectionFactory = getConnectionFactory();
if (withRedisDriver != null && redisConnectionFactory != null) {
boolean valid = true;
for (RedisDriver driver : withRedisDriver.value()) {
valid &= driver.matches(redisConnectionFactory);
if (!valid) {
throw new AssumptionViolatedException("not a vaild redis connection for driver: " + driver);
}
}
}
base.evaluate();
}
};
}
public abstract RedisConnectionFactory getConnectionFactory();
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2014 the original author 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
*
* http://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.test.util;
import org.springframework.data.redis.connection.ConnectionUtils;
import org.springframework.data.redis.connection.RedisConnectionFactory;
/**
* Enumerates the supported Redis driver types.
*
* @author Thomas Darimont
*/
public enum RedisDriver {
JEDIS {
@Override
public boolean matches(RedisConnectionFactory connectionFactory) {
return ConnectionUtils.isJedis(connectionFactory);
}
},
LETTUCE {
@Override
public boolean matches(RedisConnectionFactory connectionFactory) {
return ConnectionUtils.isLettuce(connectionFactory);
}
},
SRP {
@Override
public boolean matches(RedisConnectionFactory connectionFactory) {
return ConnectionUtils.isSrp(connectionFactory);
}
},
JREDIS {
@Override
public boolean matches(RedisConnectionFactory connectionFactory) {
return ConnectionUtils.isJredis(connectionFactory);
}
};
/**
* @param connectionFactory
* @return true of the given {@link RedisConnectionFactory} is supported by the current redis driver.
*/
public abstract boolean matches(RedisConnectionFactory connectionFactory);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2014 the original author 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
*
* http://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.test.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation to declare the supported {@link RedisDriver} types.
*
* @author Thomas Darimont
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WithRedisDriver {
RedisDriver[] value() default {};
}