+ add String/Value operations contract

This commit is contained in:
Costin Leau
2010-11-30 20:38:38 +02:00
parent ae51a0c9b0
commit cbc061cf4e
9 changed files with 353 additions and 208 deletions

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2010 the original author 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.keyvalue.redis.core;
import java.util.concurrent.TimeUnit;
/**
* @author Costin Leau
*/
public interface BoundValueOperations<K, V> extends KeyBound<K> {
void set(V value);
void set(V value, long timeout, TimeUnit unit);
Boolean setIfAbsent(V value);
V get();
V getAndSet(V value);
V increment(int delta);
}

View File

@@ -1,92 +0,0 @@
/*
* Copyright 2010 the original author 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.keyvalue.redis.core;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.springframework.data.keyvalue.redis.connection.DataType;
/**
* Default implementation for {@link BoundKeyOperations}.
*
* @author Costin Leau
*/
class DefaultBoundKeyOperations<K> extends DefaultKeyBound<K> implements BoundKeyOperations<K> {
private final KeyOperations<K> keyOps;
/**
* Constructs a new <code>DefaultBoundKeyOperations</code> instance.
*
* @param key
*/
public DefaultBoundKeyOperations(K key, KeyOperations<K> keyOps) {
super(key);
this.keyOps = keyOps;
}
@Override
public void delete() {
keyOps.delete(getKey());
}
@Override
public Boolean exists() {
return keyOps.exists(getKey());
}
@Override
public Boolean expire(long timeout, TimeUnit unit) {
return keyOps.expire(getKey(), timeout, unit);
}
@Override
public Boolean expireAt(Date date) {
return keyOps.expireAt(getKey(), date);
}
@Override
public long getExpire() {
return keyOps.getExpire(getKey());
}
@Override
public void persist() {
keyOps.persist(getKey());
}
@Override
public void rename(K newKey) {
keyOps.rename(getKey(), newKey);
setKey(newKey);
}
@Override
public Boolean renameIfAbsent(K newKey) {
if (keyOps.renameIfAbsent(getKey(), newKey)) {
setKey(newKey);
return Boolean.TRUE;
}
return Boolean.FALSE;
}
@Override
public DataType type() {
return keyOps.type(getKey());
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2010 the original author 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.keyvalue.redis.core;
import java.util.concurrent.TimeUnit;
/**
* @author Costin Leau
*/
class DefaultBoundValueOperations<K, V> extends DefaultKeyBound<K> implements BoundValueOperations<K, V> {
private final ValueOperations<K, V> ops;
/**
* Constructs a new <code>DefaultBoundValueOperations</code> instance.
*
* @param key
* @param template
*/
public DefaultBoundValueOperations(K key, RedisTemplate<K, V> template) {
super(key);
this.ops = template.valueOps();
}
@Override
public V get() {
return ops.get(getKey());
}
@Override
public V getAndSet(V value) {
return ops.getAndSet(getKey(), value);
}
@Override
public V increment(int delta) {
return ops.increment(getKey(), delta);
}
@Override
public void set(V value, long timeout, TimeUnit unit) {
ops.set(getKey(), value, timeout, unit);
}
@Override
public void set(V value) {
ops.set(getKey(), value);
}
@Override
public Boolean setIfAbsent(V value) {
return ops.setIfAbsent(getKey(), value);
}
}

View File

@@ -15,6 +15,13 @@
*/
package org.springframework.data.keyvalue.redis.core;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.data.keyvalue.redis.connection.DataType;
/**
* Basic set of Redis operations, implemented by {@link RedisTemplate}.
@@ -23,11 +30,27 @@ package org.springframework.data.keyvalue.redis.core;
*/
public interface RedisOperations<K, V> {
void set(K key, V value);
Boolean exists(K key);
V get(K key);
void delete(Collection<K> key);
V getAndSet(K key, V newValue);
DataType type(K key);
Set<K> keys(String pattern);
K randomKey();
void rename(K oldKey, K newKey);
Boolean renameIfAbsent(K oldKey, K newKey);
Boolean expire(K key, long timeout, TimeUnit unit);
Boolean expireAt(K key, Date date);
void persist(K key);
long getExpire(K key);
void watch(K... keys);
@@ -35,9 +58,9 @@ public interface RedisOperations<K, V> {
Object exec();
Integer increment(K key, int delta);
ValueOperations<K, V> valueOps();
void delete(K... keys);
BoundValueOperations<K, V> forValue(K key);
ListOperations<K, V> listOps();

View File

@@ -22,13 +22,16 @@ import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.dao.DataAccessException;
import org.springframework.data.keyvalue.redis.connection.DataType;
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
import org.springframework.data.keyvalue.redis.serializer.RedisSerializer;
@@ -212,7 +215,7 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
}
}
private byte[] rawKey(K key) {
private byte[] rawKey(Object key) {
return (key != null ? keySerializer.serialize(key) : null);
}
@@ -230,6 +233,17 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
return rawKeys;
}
private byte[][] rawKeys(Collection<K> keys) {
final byte[][] rawKeys = new byte[keys.size()][];
int i = 0;
for (K key : keys) {
rawKeys[i++] = rawKey(key);
}
return rawKeys;
}
private <HK> byte[] rawHashKey(HK value) {
return (value != null ? hashKeySerializer.serialize(value) : null);
}
@@ -298,9 +312,9 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
// utility methods for the template internal methods
private abstract class ValueDeserializingRedisCallback implements RedisCallback<V> {
private K key;
private Object key;
public ValueDeserializingRedisCallback(K key) {
public ValueDeserializingRedisCallback(Object key) {
this.key = key;
}
@@ -331,52 +345,166 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
}
@Override
public BoundListOperations<K, V> forList(K key) {
return new DefaultBoundListOperations<K, V>(key, this);
}
public void delete(Collection<K> keys) {
final byte[][] rawKeys = rawKeys(keys);
@Override
public V get(final K key) {
return execute(new ValueDeserializingRedisCallback(key) {
execute(new RedisCallback<Object>() {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
return connection.get(rawKey);
public Object doInRedis(RedisConnection connection) {
connection.del(rawKeys);
return null;
}
}, true);
}
@Override
public V getAndSet(K key, V newValue) {
final byte[] rawValue = rawValue(newValue);
return execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
return connection.getSet(rawKey, rawValue);
}
}, true);
public Boolean exists(K key) {
throw new UnsupportedOperationException();
}
@Override
public Integer increment(K key, final int delta) {
final byte[] rawKey = rawKey(key);
return execute(new RedisCallback<Integer>() {
@Override
public Integer doInRedis(RedisConnection connection) {
if (delta == 1) {
return connection.incr(rawKey);
}
public Boolean expire(K key, long timeout, TimeUnit unit) {
throw new UnsupportedOperationException();
}
if (delta == -1) {
return connection.decr(rawKey);
}
@Override
public Boolean expireAt(K key, Date date) {
throw new UnsupportedOperationException();
}
if (delta < 0) {
return connection.decrBy(rawKey, delta);
}
//
// Value operations
//
return connection.incrBy(rawKey, delta);
}
}, true);
@Override
public long getExpire(K key) {
throw new UnsupportedOperationException();
}
@Override
public Set<K> keys(String pattern) {
throw new UnsupportedOperationException();
}
@Override
public void persist(K key) {
throw new UnsupportedOperationException();
}
@Override
public K randomKey() {
throw new UnsupportedOperationException();
}
@Override
public void rename(K oldKey, K newKey) {
throw new UnsupportedOperationException();
}
@Override
public Boolean renameIfAbsent(K oldKey, K newKey) {
throw new UnsupportedOperationException();
}
@Override
public DataType type(K key) {
throw new UnsupportedOperationException();
}
@Override
public BoundValueOperations<K, V> forValue(K key) {
return new DefaultBoundValueOperations<K, V>(key, this);
}
@Override
public ValueOperations<K, V> valueOps() {
return new DefaultValueOperations();
}
private class DefaultValueOperations implements ValueOperations<K, V> {
@Override
public V get(final Object key) {
return execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
return connection.get(rawKey);
}
}, true);
}
@Override
public V getAndSet(K key, V newValue) {
final byte[] rawValue = rawValue(newValue);
return execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
return connection.getSet(rawKey, rawValue);
}
}, true);
}
@Override
public V increment(K key, final int delta) {
final byte[] rawKey = rawKey(key);
// TODO add conversion service in here ?
return (V) execute(new RedisCallback<Integer>() {
@Override
public Integer doInRedis(RedisConnection connection) {
if (delta == 1) {
return connection.incr(rawKey);
}
if (delta == -1) {
return connection.decr(rawKey);
}
if (delta < 0) {
return connection.decrBy(rawKey, delta);
}
return connection.incrBy(rawKey, delta);
}
}, true);
}
@Override
public Collection<V> multiGet(Set<K> keys) {
throw new UnsupportedOperationException();
}
@Override
public void multiSet(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
@Override
public void multiSetIfAbsent(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
@Override
public void set(K key, V value) {
final byte[] rawValue = rawValue(value);
execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
connection.set(rawKey, rawValue);
return null;
}
}, true);
}
@Override
public void set(K key, V value, long timeout, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override
public Boolean setIfAbsent(K key, V value) {
throw new UnsupportedOperationException();
}
}
@Override
@@ -384,6 +512,12 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
return new DefaultListOperations();
}
@Override
public BoundListOperations<K, V> forList(K key) {
return new DefaultBoundListOperations<K, V>(key, this);
}
@Override
public void multi() {
execute(new RedisCallback<Object>() {
@@ -395,18 +529,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
}, true);
}
@Override
public void set(K key, V value) {
final byte[] rawValue = rawValue(value);
execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
connection.set(rawKey, rawValue);
return null;
}
}, true);
}
@Override
public void watch(K... keys) {
final byte[][] rawKeys = rawKeys(keys);
@@ -420,19 +542,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
}, true);
}
@Override
public void delete(K... keys) {
final byte[][] rawKeys = rawKeys(keys);
execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) {
connection.del(rawKeys);
return null;
}
}, true);
}
//
// List operations
//

View File

@@ -15,38 +15,33 @@
*/
package org.springframework.data.keyvalue.redis.core;
import java.util.Date;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.data.keyvalue.redis.connection.DataType;
/**
* Redis operations available for all keys.
* Redis operations for simple (or in Redis terminology 'string') values.
*
* @author Costin Leau
*/
public interface KeyOperations<K> {
public interface ValueOperations<K, V> {
Boolean exists(K key);
void set(K key, V value);
void delete(K key);
void set(K key, V value, long timeout, TimeUnit unit);
DataType type(K key);
Boolean setIfAbsent(K key, V value);
Set<K> keys(String pattern);
void multiSet(Map<? extends K, ? extends V> m);
K randomKey();
void multiSetIfAbsent(Map<? extends K, ? extends V> m);
void rename(K oldKey, K newKey);
V get(Object key);
Boolean renameIfAbsent(K oldKey, K newKey);
V getAndSet(K key, V value);
Boolean expire(K key, long timeout, TimeUnit unit);
Collection<V> multiGet(Set<K> keys);
Boolean expireAt(K key, Date date);
void persist(K key);
long getExpire(K key);
}
V increment(K key, int delta);
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.keyvalue.redis.util;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -95,7 +96,7 @@ public class DefaultRedisMap<K, V> implements RedisMap<K, V> {
@Override
public void clear() {
getOperations().delete(getKey());
getOperations().delete(Collections.singleton(getKey()));
}
@Override

View File

@@ -18,6 +18,7 @@ package org.springframework.data.keyvalue.redis.util;
import java.io.Serializable;
import org.springframework.data.keyvalue.redis.core.RedisOperations;
import org.springframework.data.keyvalue.redis.core.ValueOperations;
/**
* Atomic integer backed by Redis.
@@ -29,7 +30,8 @@ import org.springframework.data.keyvalue.redis.core.RedisOperations;
public class RedisAtomicInteger extends Number implements Serializable {
private final String key;
private RedisOperations<String, Integer> operations;
private ValueOperations<String, Integer> operations;
private RedisOperations<String, Integer> generalOps;
/**
* Constructs a new <code>RedisAtomicInteger</code> instance with an initial value of zero.
@@ -50,8 +52,9 @@ public class RedisAtomicInteger extends Number implements Serializable {
*/
public RedisAtomicInteger(String redisCounter, RedisOperations<String, Integer> operations, int initialValue) {
this.key = redisCounter;
this.operations = operations;
operations.set(redisCounter, initialValue);
this.operations = operations.valueOps();
this.generalOps = operations;
this.operations.set(redisCounter, initialValue);
}
/**
@@ -92,11 +95,11 @@ public class RedisAtomicInteger extends Number implements Serializable {
*/
public boolean compareAndSet(int expect, int update) {
for (;;) {
operations.watch(key);
generalOps.watch(key);
if (expect == get()) {
operations.multi();
generalOps.multi();
set(update);
if (operations.exec() != null) {
if (generalOps.exec() != null) {
return true;
}
}
@@ -112,11 +115,11 @@ public class RedisAtomicInteger extends Number implements Serializable {
*/
public int getAndIncrement() {
for (;;) {
operations.watch(key);
generalOps.watch(key);
int value = get();
operations.multi();
generalOps.multi();
operations.increment(key, 1);
if (operations.exec() != null) {
if (generalOps.exec() != null) {
return value;
}
}
@@ -129,11 +132,11 @@ public class RedisAtomicInteger extends Number implements Serializable {
*/
public int getAndDecrement() {
for (;;) {
operations.watch(key);
generalOps.watch(key);
int value = get();
operations.multi();
generalOps.multi();
operations.increment(key, -1);
if (operations.exec() != null) {
if (generalOps.exec() != null) {
return value;
}
}
@@ -147,11 +150,11 @@ public class RedisAtomicInteger extends Number implements Serializable {
*/
public int getAndAdd(int delta) {
for (;;) {
operations.watch(key);
generalOps.watch(key);
int value = get();
operations.multi();
generalOps.multi();
set(value + delta);
if (operations.exec() != null) {
if (generalOps.exec() != null) {
return value;
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.keyvalue.redis.util;
import java.io.Serializable;
import org.springframework.data.keyvalue.redis.core.RedisOperations;
import org.springframework.data.keyvalue.redis.core.ValueOperations;
/**
* Atomic long backed by Redis.
@@ -29,7 +30,8 @@ import org.springframework.data.keyvalue.redis.core.RedisOperations;
public class RedisAtomicLong extends Number implements Serializable {
private final String key;
private RedisOperations<String, Long> operations;
private ValueOperations<String, Long> operations;
private RedisOperations<String, Long> generalOps;
/**
* Constructs a new <code>RedisAtomicLong</code> instance with an initial value of zero.
@@ -50,8 +52,8 @@ public class RedisAtomicLong extends Number implements Serializable {
*/
public RedisAtomicLong(String redisCounter, RedisOperations<String, Long> operations, long initialValue) {
this.key = redisCounter;
this.operations = operations;
operations.set(redisCounter, initialValue);
this.operations = operations.valueOps();
this.operations.set(redisCounter, initialValue);
}
/**
@@ -93,11 +95,11 @@ public class RedisAtomicLong extends Number implements Serializable {
*/
public boolean compareAndSet(long expect, long update) {
for (;;) {
operations.watch(key);
generalOps.watch(key);
if (expect == get()) {
operations.multi();
generalOps.multi();
set(update);
if (operations.exec() != null) {
if (generalOps.exec() != null) {
return true;
}
}
@@ -114,11 +116,11 @@ public class RedisAtomicLong extends Number implements Serializable {
*/
public long getAndIncrement() {
for (;;) {
operations.watch(key);
generalOps.watch(key);
long value = get();
operations.multi();
generalOps.multi();
operations.increment(key, 1);
if (operations.exec() != null) {
if (generalOps.exec() != null) {
return value;
}
}
@@ -131,11 +133,11 @@ public class RedisAtomicLong extends Number implements Serializable {
*/
public long getAndDecrement() {
for (;;) {
operations.watch(key);
generalOps.watch(key);
long value = get();
operations.multi();
generalOps.multi();
operations.increment(key, -1);
if (operations.exec() != null) {
if (generalOps.exec() != null) {
return value;
}
}
@@ -149,11 +151,11 @@ public class RedisAtomicLong extends Number implements Serializable {
*/
public long getAndAdd(long delta) {
for (;;) {
operations.watch(key);
generalOps.watch(key);
long value = get();
operations.multi();
generalOps.multi();
set(value + delta);
if (operations.exec() != null) {
if (generalOps.exec() != null) {
return value;
}
}