DATAREDIS-602 - Provide Reactive Redis Template.
Add ReactiveRedisTemplate, add tests abd add SerializerFunction and DeserializerFunction for functional Redis argument serialization. Introduce ReactiveRedisConnectionFactory and remove reactive RedisConnectionFactory methods to break the dependency to reactive types. We now no longer require Project Reactor for blocking Redis use. ReactiveRedisTemplate accepts ReactiveRedisConnectionFactory to operate on a reactive connection. The only implementation of ReactiveRedisConnectionFactory is LettuceConnectionFactory which requires by default Project Reactor. Original Pull Request: #239
This commit is contained in:
committed by
Christoph Strobl
parent
bf1a7ac8a3
commit
81657c530b
@@ -417,7 +417,7 @@ public interface ReactiveGeoCommands {
|
||||
Assert.notNull(member, "Member must not be null!");
|
||||
|
||||
return geoHash(key, Collections.singletonList(member)) //
|
||||
.map(vals -> vals.isEmpty() ? null : vals.iterator().next());
|
||||
.then(vals -> vals.isEmpty() ? Mono.empty() : Mono.justOrEmpty(vals.iterator().next()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -523,7 +523,8 @@ public interface ReactiveGeoCommands {
|
||||
|
||||
Assert.notNull(member, "Member must not be null!");
|
||||
|
||||
return geoPos(key, Collections.singletonList(member)).map(vals -> vals.isEmpty() ? null : vals.iterator().next());
|
||||
return geoPos(key, Collections.singletonList(member))
|
||||
.then(vals -> vals.isEmpty() ? Mono.empty() : Mono.justOrEmpty(vals.iterator().next()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,8 @@ 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.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -259,4 +261,353 @@ public interface ReactiveKeyCommands {
|
||||
* @see <a href="http://redis.io/commands/del">Redis Documentation: DEL</a>
|
||||
*/
|
||||
Flux<NumericResponse<List<ByteBuffer>, Long>> mDel(Publisher<List<ByteBuffer>> keys);
|
||||
|
||||
/**
|
||||
* {@code EXPIRE}/{@code PEXPIRE} command parameters.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see <a href="http://redis.io/commands/expire">Redis Documentation: EXPIRE</a>
|
||||
* @see <a href="http://redis.io/commands/pexpire">Redis Documentation: PEXPIRE</a>
|
||||
*/
|
||||
class ExpireCommand extends KeyCommand {
|
||||
|
||||
private Duration timeout;
|
||||
|
||||
private ExpireCommand(ByteBuffer key, Duration timeout) {
|
||||
|
||||
super(key);
|
||||
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpireCommand} given a {@link ByteBuffer key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return a new {@link ExpireCommand} for {@link ByteBuffer key}.
|
||||
*/
|
||||
public static ExpireCommand key(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return new ExpireCommand(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public ExpireCommand timeout(Duration timeout) {
|
||||
|
||||
Assert.notNull(timeout, "Timeout must not be null!");
|
||||
|
||||
return new ExpireCommand(getKey(), timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Duration getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set time to live for given {@code key} in seconds.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/expire">Redis Documentation: EXPIRE</a>
|
||||
*/
|
||||
default Mono<Boolean> expire(ByteBuffer key, Duration timeout) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(timeout, "Timeout must not be null!");
|
||||
|
||||
return expire(Mono.just(new ExpireCommand(key, timeout))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire {@literal keys} one by one.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} removed along with the expiration
|
||||
* result.
|
||||
* @see <a href="http://redis.io/commands/expire">Redis Documentation: EXPIRE</a>
|
||||
*/
|
||||
Flux<BooleanResponse<ExpireCommand>> expire(Publisher<ExpireCommand> commands);
|
||||
|
||||
/**
|
||||
* Set time to live for given {@code key} in milliseconds.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/pexpire">Redis Documentation: PEXPIRE</a>
|
||||
*/
|
||||
default Mono<Boolean> pExpire(ByteBuffer key, Duration timeout) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(timeout, "Timeout must not be null!");
|
||||
|
||||
return expire(Mono.just(new ExpireCommand(key, timeout))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire {@literal keys} one by one.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} removed along with the expiration
|
||||
* result.
|
||||
* @see <a href="http://redis.io/commands/pexpire">Redis Documentation: PEXPIRE</a>
|
||||
*/
|
||||
Flux<BooleanResponse<ExpireCommand>> pExpire(Publisher<ExpireCommand> commands);
|
||||
|
||||
/**
|
||||
* {@code EXPIREAT}/{@code PEXPIREAT} command parameters.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see <a href="http://redis.io/commands/expire">Redis Documentation: EXPIREAT</a>
|
||||
* @see <a href="http://redis.io/commands/pexpire">Redis Documentation: PEXPIREAT</a>
|
||||
*/
|
||||
class ExpireAtCommand extends KeyCommand {
|
||||
|
||||
private Instant expireAt;
|
||||
|
||||
private ExpireAtCommand(ByteBuffer key, Instant expireAt) {
|
||||
|
||||
super(key);
|
||||
|
||||
this.expireAt = expireAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpireAtCommand} given a {@link ByteBuffer key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return a new {@link ExpireCommand} for {@link ByteBuffer key}.
|
||||
*/
|
||||
public static ExpireAtCommand key(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return new ExpireAtCommand(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the {@literal expireAt}. Constructs a new command instance with all previously configured properties.
|
||||
*
|
||||
* @param expireAt must not be {@literal null}.
|
||||
* @return a new {@link ExpireAtCommand} with {@literal expireAt} applied.
|
||||
*/
|
||||
public ExpireAtCommand timeout(Instant expireAt) {
|
||||
|
||||
Assert.notNull(expireAt, "Expire at must not be null!");
|
||||
|
||||
return new ExpireAtCommand(getKey(), expireAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Instant getExpireAt() {
|
||||
return expireAt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expiration for given {@code key} as a {@literal UNIX} timestamp.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param expireAt must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/expireat">Redis Documentation: EXPIREAT</a>
|
||||
*/
|
||||
default Mono<Boolean> expireAt(ByteBuffer key, Instant expireAt) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(expireAt, "Expire at must not be null!");
|
||||
|
||||
return expireAt(Mono.just(new ExpireAtCommand(key, expireAt))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one-by-one the expiration for given {@code key} as a {@literal UNIX} timestamp.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} removed along with the expiration
|
||||
* result.
|
||||
* @see <a href="http://redis.io/commands/expireat">Redis Documentation: EXPIREAT</a>
|
||||
*/
|
||||
Flux<BooleanResponse<ExpireAtCommand>> expireAt(Publisher<ExpireAtCommand> commands);
|
||||
|
||||
/**
|
||||
* Set the expiration for given {@code key} as a {@literal UNIX} timestamp.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param expireAt must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/pexpireat">Redis Documentation: PEXPIREAT</a>
|
||||
*/
|
||||
default Mono<Boolean> pExpireAt(ByteBuffer key, Instant expireAt) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(expireAt, "Expire at must not be null!");
|
||||
|
||||
return pExpireAt(Mono.just(new ExpireAtCommand(key, expireAt))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one-by-one the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} removed along with the expiration
|
||||
* result.
|
||||
* @see <a href="http://redis.io/commands/pexpireat">Redis Documentation: PEXPIREAT</a>
|
||||
*/
|
||||
Flux<BooleanResponse<ExpireAtCommand>> pExpireAt(Publisher<ExpireAtCommand> commands);
|
||||
|
||||
/**
|
||||
* Remove the expiration from given {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/persist">Redis Documentation: PERSIST</a>
|
||||
*/
|
||||
default Mono<Boolean> persist(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return persist(Mono.just(new KeyCommand(key))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one-by-one the expiration from given {@code key}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} persisted along with the persist result.
|
||||
* @see <a href="http://redis.io/commands/persist">Redis Documentation: PERSIST</a>
|
||||
*/
|
||||
Flux<BooleanResponse<KeyCommand>> persist(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* Get the time to live for {@code key} in seconds.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/ttl">Redis Documentation: TTL</a>
|
||||
*/
|
||||
default Mono<Long> ttl(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return ttl(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one-by-one the time to live for keys.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link NumericResponse} holding the {@literal key} along with the time to live result.
|
||||
* @see <a href="http://redis.io/commands/ttl">Redis Documentation: TTL</a>
|
||||
*/
|
||||
Flux<NumericResponse<KeyCommand, Long>> ttl(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* Get the time to live for {@code key} in milliseconds.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/ttl">Redis Documentation: TTL</a>
|
||||
*/
|
||||
default Mono<Long> pTtl(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return pTtl(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one-by-one the time to live for keys.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link NumericResponse} holding the {@literal key} along with the time to live result.
|
||||
* @see <a href="http://redis.io/commands/pttl">Redis Documentation: PTTL</a>
|
||||
*/
|
||||
Flux<NumericResponse<KeyCommand, Long>> pTtl(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* {@code MOVE} command parameters.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see <a href="http://redis.io/commands/move">Redis Documentation: MOVE</a>
|
||||
*/
|
||||
class MoveCommand extends KeyCommand {
|
||||
|
||||
private Integer database;
|
||||
|
||||
private MoveCommand(ByteBuffer key, Integer database) {
|
||||
|
||||
super(key);
|
||||
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MoveCommand} given a {@link ByteBuffer key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return a new {@link ExpireCommand} for {@link ByteBuffer key}.
|
||||
*/
|
||||
public static MoveCommand key(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return new MoveCommand(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the {@literal database} index. Constructs a new command instance with all previously configured
|
||||
* properties.
|
||||
*
|
||||
* @param database
|
||||
* @return a new {@link MoveCommand} with {@literal database} applied.
|
||||
*/
|
||||
public MoveCommand timeout(int database) {
|
||||
return new MoveCommand(getKey(), database);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Integer getDatabase() {
|
||||
return database;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move given {@code key} to database with {@code index}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/move">Redis Documentation: MOVE</a>
|
||||
*/
|
||||
default Mono<Boolean> move(ByteBuffer key, int database) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return move(Mono.just(new MoveCommand(key, database))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move keys one-by-one between databases.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} to move along with the move result.
|
||||
* @see <a href="http://redis.io/commands/move">Redis Documentation: MOVE</a>
|
||||
*/
|
||||
Flux<BooleanResponse<MoveCommand>> move(Publisher<MoveCommand> commands);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
@@ -47,6 +48,9 @@ import lombok.Data;
|
||||
*/
|
||||
public interface ReactiveRedisConnection extends Closeable {
|
||||
|
||||
@Override
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Get {@link ReactiveKeyCommands}.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.connection;
|
||||
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
|
||||
/**
|
||||
* Thread-safe factory of reactive Redis connections.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
* @see reactor.core.publisher.Mono
|
||||
* @see reactor.core.publisher.Flux
|
||||
* @see ReactiveRedisConnection
|
||||
* @see ReactiveRedisClusterConnection
|
||||
*/
|
||||
public interface ReactiveRedisConnectionFactory extends PersistenceExceptionTranslator {
|
||||
|
||||
/**
|
||||
* @return a reactive Redis connection.
|
||||
* @since 2.0.
|
||||
*/
|
||||
ReactiveRedisConnection getReactiveConnection();
|
||||
|
||||
/**
|
||||
* @return a reactive Redis Cluster connection.
|
||||
* @since 2.0
|
||||
*/
|
||||
ReactiveRedisClusterConnection getReactiveClusterConnection();
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public interface ReactiveZSetCommands {
|
||||
* @param tuples must not be {@literal null}.
|
||||
* @return a new {@link ZAddCommand} for {@link Tuple}.
|
||||
*/
|
||||
public static ZAddCommand tuples(Collection<Tuple> tuples) {
|
||||
public static ZAddCommand tuples(Collection<? extends Tuple> tuples) {
|
||||
|
||||
Assert.notNull(tuples, "Tuples must not be null!");
|
||||
|
||||
@@ -197,6 +197,22 @@ public interface ReactiveZSetCommands {
|
||||
.map(resp -> resp.getOutput().longValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@literal tuples} to a sorted set at {@literal key}, or update their score if it already exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param tuples must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zadd">Redis Documentation: ZADD</a>
|
||||
*/
|
||||
default Mono<Long> zAdd(ByteBuffer key, Collection<? extends Tuple> tuples) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(tuples, "Tuples must not be null!");
|
||||
|
||||
return zAdd(Mono.just(ZAddCommand.tuples(tuples).to(key))).next().map(resp -> resp.getOutput().longValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link ZAddCommand#getTuples()} to a sorted set at {@link ZAddCommand#getKey()}, or update its {@literal score}
|
||||
* if it already exists.
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
|
||||
/**
|
||||
* Thread-safe factory of Redis connections.
|
||||
*
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@@ -28,45 +28,32 @@ public interface RedisConnectionFactory extends PersistenceExceptionTranslator {
|
||||
|
||||
/**
|
||||
* Provides a suitable connection for interacting with Redis.
|
||||
*
|
||||
*
|
||||
* @return connection for interacting with Redis.
|
||||
*/
|
||||
RedisConnection getConnection();
|
||||
|
||||
/**
|
||||
* Provides a suitable connection for interacting with Redis Cluster.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
RedisClusterConnection getClusterConnection();
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 2.0.
|
||||
*/
|
||||
ReactiveRedisConnection getReactiveConnection();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* @since 2.0
|
||||
*/
|
||||
ReactiveRedisClusterConnection getReactiveClusterConnection();
|
||||
|
||||
/**
|
||||
* Specifies if pipelined results should be converted to the expected data type. If false, results of
|
||||
* {@link RedisConnection#closePipeline()} and {RedisConnection#exec()} will be of the type returned by the underlying
|
||||
* driver This method is mostly for backwards compatibility with 1.0. It is generally always a good idea to allow
|
||||
* results to be converted and deserialized. In fact, this is now the default behavior.
|
||||
*
|
||||
*
|
||||
* @return Whether or not to convert pipeline and tx results
|
||||
*/
|
||||
boolean getConvertPipelineAndTxResults();
|
||||
|
||||
/**
|
||||
* Provides a suitable connection for interacting with Redis Sentinel.
|
||||
*
|
||||
*
|
||||
* @return connection for interacting with Redis Sentinel.
|
||||
* @since 1.4
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,16 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import redis.clients.jedis.HostAndPort;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisCluster;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import redis.clients.jedis.JedisSentinelPool;
|
||||
import redis.clients.jedis.JedisShardInfo;
|
||||
import redis.clients.jedis.Protocol;
|
||||
import redis.clients.util.Pool;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -33,22 +43,19 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.redis.ExceptionTranslationStrategy;
|
||||
import org.springframework.data.redis.PassThroughExceptionTranslationStrategy;
|
||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
import org.springframework.data.redis.connection.*;
|
||||
import org.springframework.data.redis.connection.ClusterCommandExecutor;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisClusterConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConnection;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import redis.clients.jedis.HostAndPort;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisCluster;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import redis.clients.jedis.JedisSentinelPool;
|
||||
import redis.clients.jedis.JedisShardInfo;
|
||||
import redis.clients.jedis.Protocol;
|
||||
import redis.clients.util.Pool;
|
||||
|
||||
/**
|
||||
* Connection factory creating <a href="http://github.com/xetorthio/jedis">Jedis</a> based connections.
|
||||
*
|
||||
@@ -358,24 +365,11 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
return new JedisClusterConnection(cluster, clusterCommandExecutor);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConnectionFactory#getReactiveConnection()
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
|
||||
*/
|
||||
@Override
|
||||
public ReactiveRedisConnection getReactiveConnection() {
|
||||
throw new UnsupportedOperationException("Jedis does not support racative connections");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveRedisClusterConnection getReactiveClusterConnection() {
|
||||
throw new UnsupportedOperationException("Jedis does not support racative connections");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
|
||||
*/
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return EXCEPTION_TRANSLATION.translate(ex);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
* Copyright 2011-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -30,15 +29,7 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.redis.ExceptionTranslationStrategy;
|
||||
import org.springframework.data.redis.PassThroughExceptionTranslationStrategy;
|
||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
import org.springframework.data.redis.connection.ClusterCommandExecutor;
|
||||
import org.springframework.data.redis.connection.Pool;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisClusterConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConnection;
|
||||
import org.springframework.data.redis.connection.*;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -70,7 +61,8 @@ import com.lambdaworks.redis.resource.ClientResources;
|
||||
* @author Mark Paluch
|
||||
* @author Balázs Németh
|
||||
*/
|
||||
public class LettuceConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
|
||||
public class LettuceConnectionFactory
|
||||
implements InitializingBean, DisposableBean, RedisConnectionFactory, ReactiveRedisConnectionFactory {
|
||||
|
||||
public static final String PING_REPLY = "PONG";
|
||||
|
||||
@@ -206,13 +198,17 @@ public class LettuceConnectionFactory implements InitializingBean, DisposableBea
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConnectionFactory#getReactiveConnection()
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnectionFactory#getReactiveConnection()
|
||||
*/
|
||||
@Override
|
||||
public LettuceReactiveRedisConnection getReactiveConnection() {
|
||||
return new LettuceReactiveRedisConnection(client);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnectionFactory#getReactiveClusterConnection()
|
||||
*/
|
||||
@Override
|
||||
public LettuceReactiveRedisClusterConnection getReactiveClusterConnection() {
|
||||
if(!isClusterAware()) {
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,9 +33,6 @@ import org.springframework.util.Assert;
|
||||
import com.lambdaworks.redis.RedisException;
|
||||
import com.lambdaworks.redis.api.reactive.RedisKeyReactiveCommands;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
@@ -130,4 +130,12 @@ public class LettuceReactiveClusterKeyCommands extends LettuceReactiveKeyCommand
|
||||
return result.map(val -> new BooleanResponse<>(command, val));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceReactiveKeyCommands#move(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<MoveCommand>> move(Publisher<MoveCommand> commands) {
|
||||
throw new UnsupportedOperationException("MOVE not supported in CLUSTER mode!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 the original author 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,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -29,13 +32,11 @@ import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiVa
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import com.lambdaworks.redis.api.reactive.RedisKeyReactiveCommands;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
|
||||
@@ -44,7 +45,7 @@ public class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveKeyCommands}.
|
||||
*
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveKeyCommands(LettuceReactiveRedisConnection connection) {
|
||||
@@ -86,37 +87,6 @@ public class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#del(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> del(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap((command) -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return cmd.del(command.getKey()).map((value) -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#mDel(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<List<ByteBuffer>, Long>> mDel(Publisher<List<ByteBuffer>> keysCollection) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(keysCollection).flatMap((keys) -> {
|
||||
|
||||
Assert.notEmpty(keys, "Keys must not be null!");
|
||||
|
||||
return cmd.del(keys.stream().collect(Collectors.toList()).toArray(new ByteBuffer[keys.size()]))
|
||||
.map((value) -> new NumericResponse<>(keys, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#keys(org.reactivestreams.Publisher)
|
||||
@@ -126,7 +96,7 @@ public class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
|
||||
return connection.execute(cmd -> Flux.from(patterns).flatMap(pattern -> {
|
||||
|
||||
Assert.notNull(pattern, "Pattern must not be null!");
|
||||
|
||||
// TODO: stream elements instead of collection
|
||||
return cmd.keys(pattern).collectList().map(value -> new MultiValueResponse<>(pattern, value));
|
||||
}));
|
||||
}
|
||||
@@ -172,4 +142,156 @@ public class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
|
||||
return cmd.renamenx(command.getKey(), command.getNewName()).map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#del(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> del(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap((command) -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return cmd.del(command.getKey()).map((value) -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#mDel(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<List<ByteBuffer>, Long>> mDel(Publisher<List<ByteBuffer>> keysCollection) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(keysCollection).flatMap((keys) -> {
|
||||
|
||||
Assert.notEmpty(keys, "Keys must not be null!");
|
||||
|
||||
return cmd.del(keys.stream().collect(Collectors.toList()).toArray(new ByteBuffer[keys.size()]))
|
||||
.map((value) -> new NumericResponse<>(keys, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveKeyCommands#expire(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<ExpireCommand>> expire(Publisher<ExpireCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(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));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveKeyCommands#pExpire(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<ExpireCommand>> pExpire(Publisher<ExpireCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getTimeout(), "Timeout must not be null!");
|
||||
|
||||
return cmd.pexpire(command.getKey(), command.getTimeout().getSeconds())
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveKeyCommands#expireAt(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<ExpireAtCommand>> expireAt(Publisher<ExpireAtCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getExpireAt(), "Expire at must not be null!");
|
||||
|
||||
return cmd.expireat(command.getKey(), command.getExpireAt().getEpochSecond())
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveKeyCommands#pExpireAt(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<ExpireAtCommand>> pExpireAt(Publisher<ExpireAtCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getExpireAt(), "Expire at must not be null!");
|
||||
|
||||
return cmd.expireat(command.getKey(), command.getExpireAt().toEpochMilli())
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveKeyCommands#persist(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<KeyCommand>> persist(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return cmd.persist(command.getKey()).map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveKeyCommands#ttl(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> ttl(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return cmd.ttl(command.getKey()).map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveKeyCommands#pTtl(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> pTtl(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return cmd.pttl(command.getKey()).map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveKeyCommands#move(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<MoveCommand>> move(Publisher<MoveCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getDatabase(), "Database must not be null!");
|
||||
|
||||
return cmd.move(command.getKey(), command.getDatabase()).map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 the original author 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,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
@@ -42,9 +45,6 @@ import com.lambdaworks.redis.ZStoreArgs;
|
||||
import com.lambdaworks.redis.codec.StringCodec;
|
||||
import com.lambdaworks.redis.protocol.LettuceCharsets;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
@@ -258,7 +258,7 @@ public class LettuceReactiveZSetCommands implements ReactiveZSetCommands {
|
||||
}
|
||||
} else {
|
||||
|
||||
Range<Number> range = ArgumentConverters.toRevRange(command.getRange());
|
||||
Range<Number> range = ArgumentConverters.toRange(command.getRange());
|
||||
|
||||
if (command.isWithScores()) {
|
||||
|
||||
@@ -447,14 +447,14 @@ public class LettuceReactiveZSetCommands implements ReactiveZSetCommands {
|
||||
result = cmd.zrangebylex(command.getKey(), ArgumentConverters.toRange(command.getRange()),
|
||||
LettuceConverters.toLimit(command.getLimit()));
|
||||
} else {
|
||||
result = cmd.zrevrangebylex(command.getKey(), ArgumentConverters.toRevRange(command.getRange()),
|
||||
result = cmd.zrevrangebylex(command.getKey(), ArgumentConverters.toRange(command.getRange()),
|
||||
LettuceConverters.toLimit(command.getLimit()));
|
||||
}
|
||||
} else {
|
||||
if (ObjectUtils.nullSafeEquals(command.getDirection(), Direction.ASC)) {
|
||||
result = cmd.zrangebylex(command.getKey(), ArgumentConverters.toRange(command.getRange()));
|
||||
} else {
|
||||
result = cmd.zrevrangebylex(command.getKey(), ArgumentConverters.toRevRange(command.getRange()));
|
||||
result = cmd.zrevrangebylex(command.getKey(), ArgumentConverters.toRange(command.getRange()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,10 +507,6 @@ public class LettuceReactiveZSetCommands implements ReactiveZSetCommands {
|
||||
return Range.from(lowerBoundArgOf(range), upperBoundArgOf(range));
|
||||
}
|
||||
|
||||
static <T> Range<T> toRevRange(org.springframework.data.domain.Range<?> range) {
|
||||
return Range.from(upperBoundArgOf(range), lowerBoundArgOf(range));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> Boundary<T> lowerBoundArgOf(org.springframework.data.domain.Range<?> range) {
|
||||
return (Boundary<T>) rangeToBoundArgumentConverter(false).convert(range);
|
||||
|
||||
@@ -33,9 +33,9 @@ class CloseSuppressingInvocationHandler implements InvocationHandler {
|
||||
private static final String HASH_CODE = "hashCode";
|
||||
private static final String EQUALS = "equals";
|
||||
|
||||
private final RedisConnection target;
|
||||
private final Object target;
|
||||
|
||||
public CloseSuppressingInvocationHandler(RedisConnection target) {
|
||||
public CloseSuppressingInvocationHandler(Object target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.geo.Circle;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.GeoResult;
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.redis.connection.ReactiveGeoCommands;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ReactiveGeoOperations}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultReactiveGeoOperations<K, V> implements ReactiveGeoOperations<K, V> {
|
||||
|
||||
private final ReactiveRedisTemplate<?, ?> template;
|
||||
private final ReactiveSerializationContext<K, V> serializationContext;
|
||||
|
||||
public DefaultReactiveGeoOperations(ReactiveRedisTemplate<?, ?> template,
|
||||
ReactiveSerializationContext<K, V> serializationContext) {
|
||||
|
||||
Assert.notNull(template, "ReactiveRedisTemplate must not be null!");
|
||||
Assert.notNull(serializationContext, "ReactiveSerializationContext must not be null!");
|
||||
|
||||
this.template = template;
|
||||
this.serializationContext = serializationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoAdd(java.lang.Object, org.springframework.data.geo.Point, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> geoAdd(K key, Point point, V member) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(point, "Point must not be null!");
|
||||
Assert.notNull(member, "Member must not be null!");
|
||||
|
||||
return createMono(connection -> connection.geoAdd(rawKey(key), point, rawValue(member)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoAdd(java.lang.Object, org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> geoAdd(K key, GeoLocation<V> location) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(location, "GeoLocation must not be null!");
|
||||
|
||||
return createMono(connection -> connection.geoAdd(rawKey(key),
|
||||
new GeoLocation<>(rawValue(location.getName()), location.getPoint())));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoAdd(java.lang.Object, java.util.Map)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> geoAdd(K key, Map<V, Point> memberCoordinateMap) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(memberCoordinateMap, "Map must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
Mono<List<GeoLocation<ByteBuffer>>> serializedList = Flux
|
||||
.fromIterable(() -> memberCoordinateMap.entrySet().iterator())
|
||||
.map(entry -> new GeoLocation<>(rawValue(entry.getKey()), entry.getValue())).collectList();
|
||||
|
||||
return serializedList.flatMap(list -> connection.geoAdd(rawKey(key), list));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoAdd(java.lang.Object, java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> geoAdd(K key, Iterable<GeoLocation<V>> geoLocations) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(geoLocations, "GeoLocations must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
Mono<List<GeoLocation<ByteBuffer>>> serializedList = Flux.fromIterable(geoLocations)
|
||||
.map(location -> new GeoLocation<>(rawValue(location.getName()), location.getPoint())).collectList();
|
||||
|
||||
return serializedList.flatMap(list -> connection.geoAdd(rawKey(key), list));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoAdd(java.lang.Object, org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<Long> geoAdd(K key, Publisher<? extends Collection<GeoLocation<V>>> locations) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(locations, "GeoLocations must not be null!");
|
||||
|
||||
return createFlux(connection -> {
|
||||
|
||||
return Flux.from(locations)
|
||||
.map(locationList -> locationList.stream()
|
||||
.map(location -> new GeoLocation<>(rawValue(location.getName()), location.getPoint()))
|
||||
.collect(Collectors.toList()))
|
||||
.flatMap(list -> connection.geoAdd(rawKey(key), list));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoDist(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Distance> geoDist(K key, V member1, V member2) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(member1, "Member 1 must not be null!");
|
||||
Assert.notNull(member2, "Member 2 must not be null!");
|
||||
|
||||
return createMono(connection -> connection.geoDist(rawKey(key), rawValue(member1), rawValue(member2)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoDist(java.lang.Object, java.lang.Object, java.lang.Object, org.springframework.data.geo.Metric)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Distance> geoDist(K key, V member1, V member2, Metric metric) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(member1, "Member 1 must not be null!");
|
||||
Assert.notNull(member2, "Member 2 must not be null!");
|
||||
Assert.notNull(metric, "Metric must not be null!");
|
||||
|
||||
return createMono(connection -> connection.geoDist(rawKey(key), rawValue(member1), rawValue(member2), metric));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoHash(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<String> geoHash(K key, V member) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(member, "Member must not be null!");
|
||||
|
||||
return createMono(connection -> connection.geoHash(rawKey(key), rawValue(member)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoHash(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final Mono<List<String>> geoHash(K key, V... members) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(members, "Member must not be null!");
|
||||
Assert.notEmpty(members, "Members must not be empty!");
|
||||
Assert.noNullElements(members, "Members must not contain null elements!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray(members) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.geoHash(rawKey(key), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoPos(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Point> geoPos(K key, V member) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(member, "Member must not be null!");
|
||||
|
||||
return createMono(connection -> connection.geoPos(rawKey(key), rawValue(member)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoPos(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final Mono<List<Point>> geoPos(K key, V... members) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(members, "Member must not be null!");
|
||||
Assert.notEmpty(members, "Members must not be empty!");
|
||||
Assert.noNullElements(members, "Members must not contain null elements!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray(members) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.geoPos(rawKey(key), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoRadius(java.lang.Object, org.springframework.data.geo.Circle)
|
||||
*/
|
||||
@Override
|
||||
public Mono<List<GeoLocation<V>>> geoRadius(K key, Circle within) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(within, "Circle must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return connection.geoRadius(rawKey(key), within) //
|
||||
.flatMap(Flux::fromIterable) //
|
||||
.map(location -> new GeoLocation<>(readValue(location.getName()), location.getPoint())) //
|
||||
.collectList();
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoRadius(java.lang.Object, org.springframework.data.geo.Circle, org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs)
|
||||
*/
|
||||
@Override
|
||||
public Mono<GeoResults<GeoLocation<V>>> geoRadius(K key, Circle within, GeoRadiusCommandArgs args) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(within, "Circle must not be null!");
|
||||
Assert.notNull(args, "GeoRadiusCommandArgs must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return connection.geoRadius(rawKey(key), within, args) //
|
||||
.flatMap(Flux::fromIterable) //
|
||||
.map(geoResult -> new GeoResult<>(
|
||||
new GeoLocation<>(readValue(geoResult.getContent().getName()), geoResult.getContent().getPoint()),
|
||||
geoResult.getDistance())) //
|
||||
.collectList() //
|
||||
.map(GeoResults::new);
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoRadiusByMember(java.lang.Object, java.lang.Object, double)
|
||||
*/
|
||||
@Override
|
||||
public Mono<List<GeoLocation<V>>> geoRadiusByMember(K key, V member, double radius) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(member, "Member must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return connection.geoRadiusByMember(rawKey(key), rawValue(member), new Distance(radius)) //
|
||||
.flatMap(Flux::fromIterable) //
|
||||
.map(geoLocation -> new GeoLocation<>(readValue(geoLocation.getName()), geoLocation.getPoint())) //
|
||||
.collectList();
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoRadiusByMember(java.lang.Object, java.lang.Object, org.springframework.data.geo.Distance)
|
||||
*/
|
||||
@Override
|
||||
public Mono<List<GeoLocation<V>>> geoRadiusByMember(K key, V member, Distance distance) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(member, "Member must not be null!");
|
||||
Assert.notNull(distance, "Distance must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return connection.geoRadiusByMember(rawKey(key), rawValue(member), distance) //
|
||||
.flatMap(Flux::fromIterable) //
|
||||
.map(geoLocation -> new GeoLocation<>(readValue(geoLocation.getName()), geoLocation.getPoint())) //
|
||||
.collectList();
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoRadiusByMember(java.lang.Object, java.lang.Object, org.springframework.data.geo.Distance, org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs)
|
||||
*/
|
||||
@Override
|
||||
public Mono<GeoResults<GeoLocation<V>>> geoRadiusByMember(K key, V member, Distance distance,
|
||||
GeoRadiusCommandArgs args) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(member, "Member must not be null!");
|
||||
Assert.notNull(distance, "Distance must not be null!");
|
||||
Assert.notNull(args, "GeoRadiusCommandArgs must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return connection.geoRadiusByMember(rawKey(key), rawValue(member), distance, args) //
|
||||
.flatMap(Flux::fromIterable) //
|
||||
.map(geoResult -> new GeoResult<>(
|
||||
new GeoLocation<>(readValue(geoResult.getContent().getName()), geoResult.getContent().getPoint()),
|
||||
geoResult.getDistance())) //
|
||||
.collectList() //
|
||||
.map(GeoResults::new);
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#geoRemove(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final Mono<Long> geoRemove(K key, V... members) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(members, "Member must not be null!");
|
||||
Assert.notEmpty(members, "Members must not be empty!");
|
||||
Assert.noNullElements(members, "Members must not contain null elements!");
|
||||
|
||||
return template.createMono(connection -> {
|
||||
|
||||
return Flux.fromArray(members) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.zSetCommands().zRem(rawKey(key), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveGeoOperations#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> delete(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return template.createMono(connection -> connection.keyCommands().del(rawKey(key))).map(l -> l != 0);
|
||||
}
|
||||
|
||||
private <T> Mono<T> createMono(Function<ReactiveGeoCommands, Publisher<T>> function) {
|
||||
|
||||
Assert.notNull(function, "Function must not be null!");
|
||||
|
||||
return template.createMono(connection -> function.apply(connection.geoCommands()));
|
||||
}
|
||||
|
||||
private <T> Flux<T> createFlux(Function<ReactiveGeoCommands, Publisher<T>> function) {
|
||||
|
||||
Assert.notNull(function, "Function must not be null!");
|
||||
|
||||
return template.createFlux(connection -> function.apply(connection.geoCommands()));
|
||||
}
|
||||
|
||||
private ByteBuffer rawKey(K key) {
|
||||
return serializationContext.key().write(key);
|
||||
}
|
||||
|
||||
private ByteBuffer rawValue(V value) {
|
||||
return serializationContext.value().write(value);
|
||||
}
|
||||
|
||||
private V readValue(ByteBuffer buffer) {
|
||||
return serializationContext.value().read(buffer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveHashCommands;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ReactiveHashOperations}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultReactiveHashOperations<H, HK, HV> implements ReactiveHashOperations<H, HK, HV> {
|
||||
|
||||
private final ReactiveRedisTemplate<?, ?> template;
|
||||
private final ReactiveSerializationContext<H, ?> serializationContext;
|
||||
|
||||
public DefaultReactiveHashOperations(ReactiveRedisTemplate<?, ?> template,
|
||||
ReactiveSerializationContext<H, ?> serializationContext) {
|
||||
|
||||
Assert.notNull(template, "ReactiveRedisTemplate must not be null!");
|
||||
Assert.notNull(serializationContext, "ReactiveSerializationContext must not be null!");
|
||||
|
||||
this.template = template;
|
||||
this.serializationContext = serializationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#delete(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Long> remove(H key, Object... hashKeys) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(hashKeys, "Hash keys must not be null!");
|
||||
Assert.notEmpty(hashKeys, "Hash keys must not be empty!");
|
||||
Assert.noNullElements(hashKeys, "Hash keys must not contain null elements!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray(hashKeys) //
|
||||
.map(o -> (HK) o).map(this::rawHashKey) //
|
||||
.collectList() //
|
||||
.then(hks -> connection.hDel(rawKey(key), hks));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#hasKey(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Boolean> hasKey(H key, Object hashKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(hashKey, "Hash key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.hExists(rawKey(key), rawHashKey((HK) hashKey)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#get(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<HV> get(H key, Object hashKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(hashKey, "Hash key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.hGet(rawKey(key), rawHashKey((HK) hashKey)).map(this::readHashValue));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#multiGet(java.lang.Object, java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Mono<List<HV>> multiGet(H key, Collection<HK> hashKeys) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(hashKeys, "Hash keys must not be null!");
|
||||
Assert.notEmpty(hashKeys, "Hash keys must not be empty!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(hashKeys) //
|
||||
.map(this::rawHashKey) //
|
||||
.collectList() //
|
||||
.then(hks -> connection.hMGet(rawKey(key), hks)).map(byteBuffers -> {
|
||||
|
||||
List<HV> values = new ArrayList<HV>(byteBuffers.size());
|
||||
for (ByteBuffer byteBuffer : byteBuffers) {
|
||||
values.add(readHashValue(byteBuffer));
|
||||
}
|
||||
return values;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#increment(java.lang.Object, java.lang.Object, long)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> 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.createMono(connection -> connection //
|
||||
.numberCommands() //
|
||||
.hIncrBy(rawKey(key), rawHashKey(hashKey), delta));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#increment(java.lang.Object, java.lang.Object, double)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Double> 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.createMono(connection -> connection //
|
||||
.numberCommands() //
|
||||
.hIncrBy(rawKey(key), rawHashKey(hashKey), delta));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#keys(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<List<HK>> keys(H key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.hKeys(rawKey(key)) //
|
||||
.flatMap(Flux::fromIterable) //
|
||||
.map(this::readHashKey) //
|
||||
.collectList());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#size(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> size(H key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.hLen(rawKey(key)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#putAll(java.lang.Object, java.util.Map)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> putAll(H key, Map<? extends HK, ? extends HV> map) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(map, "Map must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(() -> map.entrySet().iterator()) //
|
||||
.collectMap(entry -> rawHashKey(entry.getKey()), entry -> rawHashValue(entry.getValue())) //
|
||||
.flatMap(serialized -> connection.hMSet(rawKey(key), serialized));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#put(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> put(H key, HK hashKey, HV value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(hashKey, "Hash key must not be null!");
|
||||
Assert.notNull(value, "Hash value must not be null!");
|
||||
|
||||
return createMono(connection -> connection.hSet(rawKey(key), rawHashKey(hashKey), rawHashValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#putIfAbsent(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> putIfAbsent(H key, HK hashKey, HV value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(hashKey, "Hash key must not be null!");
|
||||
Assert.notNull(value, "Hash value must not be null!");
|
||||
|
||||
return createMono(connection -> connection.hSetNX(rawKey(key), rawHashKey(hashKey), rawHashValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#values(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<List<HV>> values(H key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.hVals(rawKey(key)) //
|
||||
.flatMap(Flux::fromIterable) //
|
||||
.map(this::readHashValue) //
|
||||
.collectList());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#entries(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Map<HK, HV>> entries(H key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.hGetAll(rawKey(key)) //
|
||||
.map(map -> {
|
||||
|
||||
Map<HK, HV> deserialized = new LinkedHashMap<>(map.size());
|
||||
|
||||
map.forEach((k, v) -> {
|
||||
deserialized.put(readHashKey(k), readHashValue(v));
|
||||
});
|
||||
|
||||
return deserialized;
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHashOperations#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> delete(H key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return template.createMono(connection -> connection.keyCommands().del(rawKey(key))).map(l -> l != 0);
|
||||
}
|
||||
|
||||
private <T> Mono<T> createMono(Function<ReactiveHashCommands, Publisher<T>> function) {
|
||||
|
||||
Assert.notNull(function, "Function must not be null!");
|
||||
|
||||
return template.createMono(connection -> function.apply(connection.hashCommands()));
|
||||
}
|
||||
|
||||
private ByteBuffer rawKey(H key) {
|
||||
return serializationContext.key().write(key);
|
||||
}
|
||||
|
||||
private ByteBuffer rawHashKey(HK key) {
|
||||
return serializationContext.hashKey().write(key);
|
||||
}
|
||||
|
||||
private ByteBuffer rawHashValue(HV key) {
|
||||
return serializationContext.hashValue().write(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private HK readHashKey(ByteBuffer value) {
|
||||
return (HK) serializationContext.hashKey().read(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private HV readHashValue(ByteBuffer value) {
|
||||
return (HV) serializationContext.hashValue().read(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveHyperLogLogCommands;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ReactiveHyperLogLogOperations}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultReactiveHyperLogLogOperations<K, V> implements ReactiveHyperLogLogOperations<K, V> {
|
||||
|
||||
private final ReactiveRedisTemplate<?, ?> template;
|
||||
private final ReactiveSerializationContext<K, V> serializationContext;
|
||||
|
||||
public DefaultReactiveHyperLogLogOperations(ReactiveRedisTemplate<?, ?> template,
|
||||
ReactiveSerializationContext<K, V> serializationContext) {
|
||||
|
||||
Assert.notNull(template, "ReactiveRedisTemplate must not be null!");
|
||||
Assert.notNull(serializationContext, "ReactiveSerializationContext must not be null!");
|
||||
|
||||
this.template = template;
|
||||
this.serializationContext = serializationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHyperLogLogOperations#add(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final Mono<Long> add(K key, V... values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.notEmpty(values, "Values must not be empty!");
|
||||
Assert.noNullElements(values, "Values must not contain null elements!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray(values) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(serializedValues -> connection.pfAdd(rawKey(key), serializedValues));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHyperLogLogOperations#size(java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final Mono<Long> size(K... keys) {
|
||||
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
Assert.notEmpty(keys, "Keys must not be empty!");
|
||||
Assert.noNullElements(keys, "Keys must not contain null elements!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray(keys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(connection::pfCount);
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHyperLogLogOperations#union(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final Mono<Boolean> union(K destination, K... sourceKeys) {
|
||||
|
||||
Assert.notNull(destination, "Destination key must not be null!");
|
||||
Assert.notNull(sourceKeys, "Source keys must not be null!");
|
||||
Assert.notEmpty(sourceKeys, "Source keys must not be empty!");
|
||||
Assert.noNullElements(sourceKeys, "Source keys must not contain null elements!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray(sourceKeys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.pfMerge(rawKey(destination), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveHyperLogLogOperations#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> delete(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return template.createMono(connection -> connection.keyCommands().del(rawKey(key))).map(l -> l != 0);
|
||||
}
|
||||
|
||||
private <T> Mono<T> createMono(Function<ReactiveHyperLogLogCommands, Publisher<T>> function) {
|
||||
|
||||
Assert.notNull(function, "Function must not be null!");
|
||||
|
||||
return template.createMono(connection -> function.apply(connection.hyperLogLogCommands()));
|
||||
}
|
||||
|
||||
private ByteBuffer rawKey(K key) {
|
||||
return serializationContext.key().write(key);
|
||||
}
|
||||
|
||||
private ByteBuffer rawValue(V value) {
|
||||
return serializationContext.value().write(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
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.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveListCommands;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ReactiveListOperations}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultReactiveListOperations<K, V> implements ReactiveListOperations<K, V> {
|
||||
|
||||
private final ReactiveRedisTemplate<?, ?> template;
|
||||
private final ReactiveSerializationContext<K, V> serializationContext;
|
||||
|
||||
public DefaultReactiveListOperations(ReactiveRedisTemplate<?, ?> template,
|
||||
ReactiveSerializationContext<K, V> serializationContext) {
|
||||
|
||||
Assert.notNull(template, "ReactiveRedisTemplate must not be null!");
|
||||
Assert.notNull(serializationContext, "ReactiveSerializationContext must not be null!");
|
||||
|
||||
this.template = template;
|
||||
this.serializationContext = serializationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#range(java.lang.Object, long, long)
|
||||
*/
|
||||
@Override
|
||||
public Mono<List<V>> range(K key, long start, long end) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lRange(rawKey(key), start, end).map(raw -> {
|
||||
|
||||
List<V> result = new ArrayList<V>(raw.size());
|
||||
|
||||
for (ByteBuffer buffer : raw) {
|
||||
result.add(readValue(buffer));
|
||||
}
|
||||
|
||||
return result;
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#trim(java.lang.Object, long, long)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> trim(K key, long start, long end) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lTrim(rawKey(key), start, end));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#size(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> size(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lLen(rawKey(key)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#leftPush(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> leftPush(K key, V value) {
|
||||
return leftPushAll(key, value);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#leftPushAll(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final Mono<Long> leftPushAll(K key, V... values) {
|
||||
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
|
||||
return leftPushAll(key, Arrays.asList(values));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#leftPushAll(java.lang.Object, java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> leftPushAll(K key, Collection<V> values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.notEmpty(values, "Values must not be empty!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(values) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.lPush(rawKey(key), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#leftPushIfPresent(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> leftPushIfPresent(K key, V value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lPushX(rawKey(key), rawValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#leftPush(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> leftPush(K key, V pivot, V value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lInsert(rawKey(key), Position.BEFORE, rawValue(pivot), rawValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#rightPush(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> rightPush(K key, V value) {
|
||||
return rightPushAll(key, value);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#rightPushAll(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final Mono<Long> rightPushAll(K key, V... values) {
|
||||
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
|
||||
return rightPushAll(key, Arrays.asList(values));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#rightPushAll(java.lang.Object, java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> rightPushAll(K key, Collection<V> values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.notEmpty(values, "Values must not be empty!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(values) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.rPush(rawKey(key), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#rightPushIfPresent(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> rightPushIfPresent(K key, V value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.rPushX(rawKey(key), rawValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#rightPush(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> rightPush(K key, V pivot, V value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lInsert(rawKey(key), Position.AFTER, rawValue(pivot), rawValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#set(java.lang.Object, long, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> set(K key, long index, V value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lSet(rawKey(key), index, rawValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#remove(java.lang.Object, long, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Long> remove(K key, long count, Object value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lRem(rawKey(key), count, rawValue((V) value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#index(java.lang.Object, long)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> index(K key, long index) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lIndex(rawKey(key), index).map(this::readValue));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#leftPop(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> leftPop(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.lPop(rawKey(key)).map(this::readValue));
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#leftPop(java.lang.Object, java.time.Duration)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> leftPop(K key, Duration timeout) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(timeout, "Duration must not be null!");
|
||||
Assert.isTrue(isZeroOrGreater1Second(timeout), "Duration must be either zero or greater or equal to 1 second");
|
||||
|
||||
return createMono(connection -> connection.blPop(Collections.singletonList(rawKey(key)), timeout)
|
||||
.map(popResult -> readValue(popResult.getValue())));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#rightPop(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> rightPop(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.rPop(rawKey(key)).map(this::readValue));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#rightPop(java.lang.Object, java.time.Duration)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> rightPop(K key, Duration timeout) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(timeout, "Duration must not be null!");
|
||||
Assert.isTrue(isZeroOrGreater1Second(timeout), "Duration must be either zero or greater or equal to 1 second");
|
||||
|
||||
return createMono(connection -> connection.brPop(Collections.singletonList(rawKey(key)), timeout)
|
||||
.map(popResult -> readValue(popResult.getValue())));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#rightPopAndLeftPush(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> rightPopAndLeftPush(K sourceKey, K destinationKey) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.rPopLPush(rawKey(sourceKey), rawKey(destinationKey)).map(this::readValue));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#rightPopAndLeftPush(java.lang.Object, java.lang.Object, java.time.Duration)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> rightPopAndLeftPush(K sourceKey, K destinationKey, Duration timeout) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(timeout, "Duration must not be null!");
|
||||
Assert.isTrue(isZeroOrGreater1Second(timeout), "Duration must be either zero or greater or equal to 1 second");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.bRPopLPush(rawKey(sourceKey), rawKey(destinationKey), timeout).map(this::readValue));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> delete(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return template.createMono(connection -> connection.keyCommands().del(rawKey(key))).map(l -> l != 0);
|
||||
}
|
||||
|
||||
private <T> Mono<T> createMono(Function<ReactiveListCommands, Publisher<T>> function) {
|
||||
|
||||
Assert.notNull(function, "Function must not be null!");
|
||||
|
||||
return template.createMono(connection -> function.apply(connection.listCommands()));
|
||||
}
|
||||
|
||||
private boolean isZeroOrGreater1Second(Duration timeout) {
|
||||
return timeout.isZero() || timeout.getNano() % TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS) == 0;
|
||||
}
|
||||
|
||||
private ByteBuffer rawKey(K key) {
|
||||
return serializationContext.key().write(key);
|
||||
}
|
||||
|
||||
private ByteBuffer rawValue(V value) {
|
||||
return serializationContext.value().write(value);
|
||||
}
|
||||
|
||||
private V readValue(ByteBuffer buffer) {
|
||||
return serializationContext.value().read(buffer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveSetCommands;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ReactiveSetOperations}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultReactiveSetOperations<K, V> implements ReactiveSetOperations<K, V> {
|
||||
|
||||
private final ReactiveRedisTemplate<?, ?> template;
|
||||
private final ReactiveSerializationContext<K, V> serializationContext;
|
||||
|
||||
public DefaultReactiveSetOperations(ReactiveRedisTemplate<?, ?> template,
|
||||
ReactiveSerializationContext<K, V> serializationContext) {
|
||||
|
||||
Assert.notNull(template, "ReactiveRedisTemplate must not be null!");
|
||||
Assert.notNull(serializationContext, "ReactiveSerializationContext must not be null!");
|
||||
|
||||
this.template = template;
|
||||
this.serializationContext = serializationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#add(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> add(K key, V... values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
if (values.length == 1) {
|
||||
return createMono(connection -> connection.sAdd(rawKey(key), rawValue(values[0])));
|
||||
}
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray(values) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.sAdd(rawKey(key), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#remove(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Long> remove(K key, Object... values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
if (values.length == 1) {
|
||||
return createMono(connection -> connection.sRem(rawKey(key), rawValue((V) values[0])));
|
||||
}
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray((V[]) values) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.sRem(rawKey(key), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#pop(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> pop(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.sPop(rawKey(key)).map(this::readValue));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#move(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> move(K sourceKey, V value, K destKey) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.sMove(rawKey(sourceKey), rawKey(destKey), rawValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#size(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> size(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.sCard(rawKey(key)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#isMember(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Boolean> isMember(K key, Object o) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.sIsMember(rawKey(key), rawValue((V) o)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#intersect(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> intersect(K key, K otherKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKey, "Other key must not be null!");
|
||||
|
||||
return intersect(key, Collections.singleton(otherKey));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#intersect(java.lang.Object, java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> intersect(K key, Collection<K> otherKeys) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKeys, "Other keys must not be null!");
|
||||
|
||||
List<K> keys = getKeys(key, otherKeys);
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(keys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(connection::sInter) //
|
||||
.map(this::readValueSet);
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#intersectAndStore(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> intersectAndStore(K key, K otherKey, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKey, "Other key must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
return intersectAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#intersectAndStore(java.lang.Object, java.util.Collection, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKeys, "Other keys must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
List<K> keys = getKeys(key, otherKeys);
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(keys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(rawKeys -> connection.sInterStore(rawKey(destKey), rawKeys));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#union(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> union(K key, K otherKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKey, "Other key must not be null!");
|
||||
|
||||
return union(key, Collections.singleton(otherKey));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#union(java.lang.Object, java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> union(K key, Collection<K> otherKeys) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKeys, "Other keys must not be null!");
|
||||
|
||||
List<K> keys = getKeys(key, otherKeys);
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(keys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(connection::sUnion) //
|
||||
.map(this::readValueSet);
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#unionAndStore(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> unionAndStore(K key, K otherKey, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKey, "Other key must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
return unionAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#unionAndStore(java.lang.Object, java.util.Collection, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKeys, "Other keys must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
List<K> keys = getKeys(key, otherKeys);
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(keys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(rawKeys -> connection.sUnionStore(rawKey(destKey), rawKeys));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#difference(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> difference(K key, K otherKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKey, "Other key must not be null!");
|
||||
|
||||
return difference(key, Collections.singleton(otherKey));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#difference(java.lang.Object, java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> difference(K key, Collection<K> otherKeys) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKeys, "Other keys must not be null!");
|
||||
|
||||
List<K> keys = getKeys(key, otherKeys);
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(keys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(connection::sDiff) //
|
||||
.map(this::readValueSet);
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#differenceAndStore(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> differenceAndStore(K key, K otherKey, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKey, "Other key must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
return differenceAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#differenceAndStore(java.lang.Object, java.util.Collection, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> differenceAndStore(K key, Collection<K> otherKeys, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKeys, "Other keys must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
List<K> keys = getKeys(key, otherKeys);
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(keys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(rawKeys -> connection.sDiffStore(rawKey(destKey), rawKeys));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#members(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> members(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.sMembers(rawKey(key)).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#randomMember(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> randomMember(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.sRandMember(rawKey(key)).map(this::readValue));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#distinctRandomMembers(java.lang.Object, long)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> distinctRandomMembers(K key, long count) {
|
||||
|
||||
Assert.isTrue(count > 0, "Negative count not supported. Use randomMembers to allow duplicate elements.");
|
||||
|
||||
return createMono(connection -> connection.sRandMember(rawKey(key), count).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#randomMembers(java.lang.Object, long)
|
||||
*/
|
||||
@Override
|
||||
public Mono<List<V>> randomMembers(K key, long count) {
|
||||
|
||||
Assert.isTrue(count > 0, "Use a positive number for count. This method is already allowing duplicate elements.");
|
||||
|
||||
return createMono(connection -> connection.sRandMember(rawKey(key), -count).map(this::readValueList));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> delete(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return template.createMono(connection -> connection.keyCommands().del(rawKey(key))).map(l -> l != 0);
|
||||
}
|
||||
|
||||
private <T> Mono<T> createMono(Function<ReactiveSetCommands, Publisher<T>> function) {
|
||||
|
||||
Assert.notNull(function, "Function must not be null!");
|
||||
|
||||
return template.createMono(connection -> function.apply(connection.setCommands()));
|
||||
}
|
||||
|
||||
private ByteBuffer rawKey(K key) {
|
||||
return serializationContext.key().write(key);
|
||||
}
|
||||
|
||||
private List<K> getKeys(K key, Collection<K> otherKeys) {
|
||||
|
||||
List<K> keys = new ArrayList<>(1 + otherKeys.size());
|
||||
|
||||
keys.add(key);
|
||||
keys.addAll(otherKeys);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
private ByteBuffer rawValue(V value) {
|
||||
return serializationContext.value().write(value);
|
||||
}
|
||||
|
||||
private V readValue(ByteBuffer buffer) {
|
||||
return serializationContext.value().read(buffer);
|
||||
}
|
||||
|
||||
private Set<V> readValueSet(Collection<ByteBuffer> raw) {
|
||||
|
||||
Set<V> result = new LinkedHashSet<>(raw.size());
|
||||
|
||||
for (ByteBuffer buffer : raw) {
|
||||
result.add(readValue(buffer));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<V> readValueList(Collection<ByteBuffer> raw) {
|
||||
|
||||
List<V> result = new ArrayList<>(raw.size());
|
||||
|
||||
for (ByteBuffer buffer : raw) {
|
||||
result.add(readValue(buffer));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveStringCommands;
|
||||
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext.SerializationTuple;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ReactiveValueOperations}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultReactiveValueOperations<K, V> implements ReactiveValueOperations<K, V> {
|
||||
|
||||
private final ReactiveRedisTemplate<?, ?> template;
|
||||
private final ReactiveSerializationContext<K, V> serializationContext;
|
||||
|
||||
public DefaultReactiveValueOperations(ReactiveRedisTemplate<?, ?> template,
|
||||
ReactiveSerializationContext<K, V> serializationContext) {
|
||||
|
||||
Assert.notNull(template, "ReactiveRedisTemplate must not be null!");
|
||||
Assert.notNull(serializationContext, "ReactiveSerializationContext must not be null!");
|
||||
|
||||
this.template = template;
|
||||
this.serializationContext = serializationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#set(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> set(K key, V value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.set(rawKey(key), rawValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#set(java.lang.Object, java.lang.Object, long, java.util.concurrent.TimeUnit)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> set(K key, V value, Duration timeout) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(timeout, "Duration must not be null!");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.set(rawKey(key), rawValue(value), Expiration.from(timeout), SetOption.UPSERT));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#setIfAbsent(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> setIfAbsent(K key, V value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.set(rawKey(key), rawValue(value), Expiration.persistent(), SetOption.SET_IF_ABSENT));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#setIfPresent(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> setIfPresent(K key, V value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.set(rawKey(key), rawValue(value), Expiration.persistent(), SetOption.SET_IF_PRESENT));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#multiSet(java.util.Map)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> multiSet(Map<? extends K, ? extends V> map) {
|
||||
|
||||
Assert.notNull(map, "Map must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
Mono<Map<ByteBuffer, ByteBuffer>> serializedMap = Flux.fromIterable(() -> map.entrySet().iterator())
|
||||
.collectMap(entry -> rawKey(entry.getKey()), entry -> rawValue(entry.getValue()));
|
||||
|
||||
return serializedMap.flatMap(connection::mSet);
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#multiSetIfAbsent(java.util.Map)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> multiSetIfAbsent(Map<? extends K, ? extends V> map) {
|
||||
|
||||
Assert.notNull(map, "Map must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
Mono<Map<ByteBuffer, ByteBuffer>> serializedMap = Flux.fromIterable(() -> map.entrySet().iterator())
|
||||
.collectMap(entry -> rawKey(entry.getKey()), entry -> rawValue(entry.getValue()));
|
||||
|
||||
return serializedMap.flatMap(connection::mSetNX);
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#get(java.lang.Object)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Mono<V> get(Object key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.get(rawKey((K) key)) //
|
||||
.map(this::readValue));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#getAndSet(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> getAndSet(K key, V value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.getSet(rawKey(key), rawValue(value)).map(value()::read));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#multiGet(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Mono<List<V>> multiGet(Collection<K> keys) {
|
||||
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
|
||||
return createMono(connection -> Flux.fromIterable(keys).map(key()::write).collectList().flatMap(connection::mGet)
|
||||
.map(byteBuffers -> {
|
||||
List<V> result = new ArrayList<>(byteBuffers.size());
|
||||
|
||||
for (ByteBuffer buffer : byteBuffers) {
|
||||
|
||||
if (buffer == null) {
|
||||
result.add(null);
|
||||
} else {
|
||||
result.add(readValue(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#append(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> append(K key, String value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return createMono(connection -> connection.append(rawKey(key), serializationContext.string().write(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#get(java.lang.Object, long, long)
|
||||
*/
|
||||
@Override
|
||||
public Mono<String> get(K key, long start, long end) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.getRange(rawKey(key), start, end) //
|
||||
.map(string()::read));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#set(java.lang.Object, java.lang.Object, long)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> set(K key, V value, long offset) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.setRange(rawKey(key), rawValue(value), offset));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#size(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> size(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.strLen(rawKey(key)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#setBit(java.lang.Object, long, boolean)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> setBit(K key, long offset, boolean value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.setBit(rawKey(key), offset, value));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#getBit(java.lang.Object, long)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> getBit(K key, long offset) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.getBit(rawKey(key), offset));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveValueOperations#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> delete(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return template.createMono(connection -> connection.keyCommands().del(rawKey(key))).map(l -> l != 0);
|
||||
}
|
||||
|
||||
private <T> Mono<T> createMono(Function<ReactiveStringCommands, Publisher<T>> function) {
|
||||
|
||||
Assert.notNull(function, "Function must not be null!");
|
||||
|
||||
return template.createMono(connection -> function.apply(connection.stringCommands()));
|
||||
}
|
||||
|
||||
private ByteBuffer rawKey(K key) {
|
||||
return serializationContext.key().write(key);
|
||||
}
|
||||
|
||||
private ByteBuffer rawValue(V value) {
|
||||
return serializationContext.value().write(value);
|
||||
}
|
||||
|
||||
private V readValue(ByteBuffer buffer) {
|
||||
return serializationContext.value().read(buffer);
|
||||
}
|
||||
|
||||
private SerializationTuple<String> string() {
|
||||
return serializationContext.string();
|
||||
}
|
||||
|
||||
private SerializationTuple<K> key() {
|
||||
return serializationContext.key();
|
||||
}
|
||||
|
||||
private SerializationTuple<V> value() {
|
||||
return serializationContext.value();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.redis.connection.DefaultTuple;
|
||||
import org.springframework.data.redis.connection.ReactiveZSetCommands;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Limit;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext;
|
||||
import org.springframework.data.redis.util.ByteUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ReactiveZSetOperations}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultReactiveZSetOperations<K, V> implements ReactiveZSetOperations<K, V> {
|
||||
|
||||
private final ReactiveRedisTemplate<?, ?> template;
|
||||
private final ReactiveSerializationContext<K, V> serializationContext;
|
||||
|
||||
public DefaultReactiveZSetOperations(ReactiveRedisTemplate<?, ?> template,
|
||||
ReactiveSerializationContext<K, V> serializationContext) {
|
||||
|
||||
Assert.notNull(template, "ReactiveRedisTemplate must not be null!");
|
||||
Assert.notNull(serializationContext, "ReactiveSerializationContext must not be null!");
|
||||
|
||||
this.template = template;
|
||||
this.serializationContext = serializationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#add(java.lang.Object, java.lang.Object, double)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> add(K key, V value, double score) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zAdd(rawKey(key), score, rawValue(value)).map(l -> l != 0));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#add(java.lang.Object, java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> addAll(K key, Collection<? extends TypedTuple<V>> tuples) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(tuples, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(tuples) //
|
||||
.map(t -> new DefaultTuple(ByteUtils.getBytes(rawValue(t.getValue())), t.getScore())) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.zAdd(rawKey(key), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#remove(java.lang.Object, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Long> remove(K key, Object... values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
|
||||
if (values.length == 1) {
|
||||
return createMono(connection -> connection.zRem(rawKey(key), rawValue((V) values[0])));
|
||||
}
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray((V[]) values) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.zRem(rawKey(key), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#incrementScore(java.lang.Object, java.lang.Object, double)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Double> incrementScore(K key, V value, double delta) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zIncrBy(rawKey(key), delta, rawValue(value)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#rank(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Long> rank(K key, Object o) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRank(rawKey(key), rawValue((V) o)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#reverseRank(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Long> reverseRank(K key, Object o) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRevRank(rawKey(key), rawValue((V) o)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#range(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> range(K key, Range<Long> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRange(rawKey(key), range).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#rangeWithScores(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<TypedTuple<V>>> rangeWithScores(K key, Range<Long> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRangeWithScores(rawKey(key), range).map(this::readTypedTupleSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#rangeByScore(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> rangeByScore(K key, Range<Double> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRangeByScore(rawKey(key), range).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#rangeByScoreWithScores(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<TypedTuple<V>>> rangeByScoreWithScores(K key, Range<Double> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.zRangeByScoreWithScores(rawKey(key), range).map(this::readTypedTupleSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#rangeByScore(java.lang.Object, org.springframework.data.domain.Range, org.springframework.data.redis.connection.RedisZSetCommands.Limit)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> rangeByScore(K key, Range<Double> range, Limit limit) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRangeByScore(rawKey(key), range, limit).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#rangeByScoreWithScores(java.lang.Object, org.springframework.data.domain.Range, org.springframework.data.redis.connection.RedisZSetCommands.Limit)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<TypedTuple<V>>> rangeByScoreWithScores(K key, Range<Double> range, Limit limit) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
Assert.notNull(limit, "Limit must not be null!");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.zRangeByScoreWithScores(rawKey(key), range, limit).map(this::readTypedTupleSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#reverseRange(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> reverseRange(K key, Range<Long> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRevRange(rawKey(key), range).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#reverseRangeWithScores(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<TypedTuple<V>>> reverseRangeWithScores(K key, Range<Long> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRevRangeWithScores(rawKey(key), range).map(this::readTypedTupleSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#reverseRangeByScore(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> reverseRangeByScore(K key, Range<Double> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRevRangeByScore(rawKey(key), range).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#reverseRangeByScoreWithScores(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<TypedTuple<V>>> reverseRangeByScoreWithScores(K key, Range<Double> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.zRevRangeByScoreWithScores(rawKey(key), range).map(this::readTypedTupleSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#reverseRangeByScore(java.lang.Object, org.springframework.data.domain.Range, org.springframework.data.redis.connection.RedisZSetCommands.Limit)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> reverseRangeByScore(K key, Range<Double> range, Limit limit) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRevRangeByScore(rawKey(key), range, limit).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#reverseRangeByScoreWithScores(java.lang.Object, org.springframework.data.domain.Range, org.springframework.data.redis.connection.RedisZSetCommands.Limit)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<TypedTuple<V>>> reverseRangeByScoreWithScores(K key, Range<Double> range, Limit limit) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
Assert.notNull(limit, "Limit must not be null!");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.zRevRangeByScoreWithScores(rawKey(key), range, limit).map(this::readTypedTupleSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#count(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> count(K key, Range<Double> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zCount(rawKey(key), range));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#size(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> size(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zCard(rawKey(key)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#score(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Double> score(K key, Object o) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zScore(rawKey(key), rawValue((V) o)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#removeRange(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> removeRange(K key, Range<Long> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRemRangeByRank(rawKey(key), range));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#removeRangeByScore(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> removeRangeByScore(K key, Range<Double> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRemRangeByScore(rawKey(key), range));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionAndStore(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> unionAndStore(K key, K otherKey, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKey, "Other key must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
return unionAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionAndStore(java.lang.Object, java.util.Collection, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKeys, "Other keys must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
List<K> keys = getKeys(key, otherKeys);
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(keys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.zUnionStore(rawKey(destKey), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersectAndStore(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> intersectAndStore(K key, K otherKey, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKey, "Other key must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
return intersectAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersectAndStore(java.lang.Object, java.util.Collection, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(otherKeys, "Other keys must not be null!");
|
||||
Assert.notNull(destKey, "Destination key must not be null!");
|
||||
|
||||
List<K> keys = getKeys(key, otherKeys);
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromIterable(keys) //
|
||||
.map(this::rawKey) //
|
||||
.collectList() //
|
||||
.flatMap(serialized -> connection.zInterStore(rawKey(destKey), serialized));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#rangeByLex(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> rangeByLex(K key, Range<String> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRangeByLex(rawKey(key), range).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#rangeByLex(java.lang.Object, org.springframework.data.domain.Range, org.springframework.data.redis.connection.RedisZSetCommands.Limit)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> rangeByLex(K key, Range<String> range, Limit limit) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
Assert.notNull(limit, "Limit must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRangeByLex(rawKey(key), range, limit).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#reverseRangeByLex(java.lang.Object, org.springframework.data.domain.Range)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> reverseRangeByLex(K key, Range<String> range) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRevRangeByLex(rawKey(key), range).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#reverseRangeByLex(java.lang.Object, org.springframework.data.domain.Range, org.springframework.data.redis.connection.RedisZSetCommands.Limit)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Set<V>> reverseRangeByLex(K key, Range<String> range, Limit limit) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(range, "Range must not be null!");
|
||||
Assert.notNull(limit, "Limit must not be null!");
|
||||
|
||||
return createMono(connection -> connection.zRevRangeByLex(rawKey(key), range, limit).map(this::readValueSet));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveZSetOperations#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> delete(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return template.createMono(connection -> connection.keyCommands().del(rawKey(key))).map(l -> l != 0);
|
||||
}
|
||||
|
||||
private <T> Mono<T> createMono(Function<ReactiveZSetCommands, Publisher<T>> function) {
|
||||
|
||||
Assert.notNull(function, "Function must not be null!");
|
||||
|
||||
return template.createMono(connection -> function.apply(connection.zSetCommands()));
|
||||
}
|
||||
|
||||
private ByteBuffer rawKey(K key) {
|
||||
return serializationContext.key().write(key);
|
||||
}
|
||||
|
||||
private List<K> getKeys(K key, Collection<K> otherKeys) {
|
||||
|
||||
List<K> keys = new ArrayList<>(1 + otherKeys.size());
|
||||
|
||||
keys.add(key);
|
||||
keys.addAll(otherKeys);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
private ByteBuffer rawValue(V value) {
|
||||
return serializationContext.value().write(value);
|
||||
}
|
||||
|
||||
private V readValue(ByteBuffer buffer) {
|
||||
return serializationContext.value().read(buffer);
|
||||
}
|
||||
|
||||
private Set<V> readValueSet(Collection<ByteBuffer> raw) {
|
||||
|
||||
Set<V> result = new LinkedHashSet<>(raw.size());
|
||||
|
||||
for (ByteBuffer buffer : raw) {
|
||||
result.add(readValue(buffer));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<TypedTuple<V>> readTypedTupleSet(Collection<Tuple> raw) {
|
||||
|
||||
Set<TypedTuple<V>> result = new LinkedHashSet<>(raw.size());
|
||||
|
||||
for (Tuple tuple : raw) {
|
||||
result.add(new DefaultTypedTuple<>(readValue(ByteBuffer.wrap(tuple.getValue())), tuple.getScore()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.geo.Circle;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
|
||||
|
||||
/**
|
||||
* Reactive Redis operations for geo commands.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see <a href="http://redis.io/commands#geo">Redis Documentation: Geo Commands</a>
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveGeoOperations<K, M> {
|
||||
|
||||
/**
|
||||
* Add {@link Point} with given member {@literal name} to {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param point must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @return Number of elements added.
|
||||
* @see <a href="http://redis.io/commands/geoadd">Redis Documentation: GEOADD</a>
|
||||
*/
|
||||
Mono<Long> geoAdd(K key, Point point, M member);
|
||||
|
||||
/**
|
||||
* Add {@link GeoLocation} to {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param location must not be {@literal null}.
|
||||
* @return Number of elements added.
|
||||
* @see <a href="http://redis.io/commands/geoadd">Redis Documentation: GEOADD</a>
|
||||
*/
|
||||
Mono<Long> geoAdd(K key, GeoLocation<M> location);
|
||||
|
||||
/**
|
||||
* Add {@link Map} of member / {@link Point} pairs to {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param memberCoordinateMap must not be {@literal null}.
|
||||
* @return Number of elements added.
|
||||
* @see <a href="http://redis.io/commands/geoadd">Redis Documentation: GEOADD</a>
|
||||
*/
|
||||
Mono<Long> geoAdd(K key, Map<M, Point> memberCoordinateMap);
|
||||
|
||||
/**
|
||||
* Add {@link GeoLocation}s to {@literal key}
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param locations must not be {@literal null}.
|
||||
* @return Number of elements added.
|
||||
* @see <a href="http://redis.io/commands/geoadd">Redis Documentation: GEOADD</a>
|
||||
*/
|
||||
Mono<Long> geoAdd(K key, Iterable<GeoLocation<M>> locations);
|
||||
|
||||
/**
|
||||
* Add {@link GeoLocation}s to {@literal key}
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param locations must not be {@literal null}.
|
||||
* @return Number of elements added.
|
||||
* @see <a href="http://redis.io/commands/geoadd">Redis Documentation: GEOADD</a>
|
||||
*/
|
||||
Flux<Long> geoAdd(K key, Publisher<? extends Collection<GeoLocation<M>>> locations);
|
||||
|
||||
/**
|
||||
* Get the {@link Distance} between {@literal member1} and {@literal member2}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member1 must not be {@literal null}.
|
||||
* @param member2 must not be {@literal null}.
|
||||
* @return can be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/geodist">Redis Documentation: GEODIST</a>
|
||||
*/
|
||||
Mono<Distance> geoDist(K key, M member1, M member2);
|
||||
|
||||
/**
|
||||
* Get the {@link Distance} between {@literal member1} and {@literal member2} in the given {@link Metric}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member1 must not be {@literal null}.
|
||||
* @param member2 must not be {@literal null}.
|
||||
* @param metric must not be {@literal null}.
|
||||
* @return can be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/geodist">Redis Documentation: GEODIST</a>
|
||||
*/
|
||||
Mono<Distance> geoDist(K key, M member1, M member2, Metric metric);
|
||||
|
||||
/**
|
||||
* Get Geohash representation of the position for one or more {@literal member}s.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/geohash">Redis Documentation: GEOHASH</a>
|
||||
*/
|
||||
Mono<String> geoHash(K key, M member);
|
||||
|
||||
/**
|
||||
* Get Geohash representation of the position for one or more {@literal member}s.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param members must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/geohash">Redis Documentation: GEOHASH</a>
|
||||
*/
|
||||
Mono<List<String>> geoHash(K key, M... members);
|
||||
|
||||
/**
|
||||
* Get the {@link Point} representation of positions for one or more {@literal member}s.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/geopos">Redis Documentation: GEOPOS</a>
|
||||
*/
|
||||
Mono<Point> geoPos(K key, M member);
|
||||
|
||||
/**
|
||||
* Get the {@link Point} representation of positions for one or more {@literal member}s.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param members must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/geopos">Redis Documentation: GEOPOS</a>
|
||||
*/
|
||||
Mono<List<Point>> geoPos(K key, M... members);
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within the boundaries of a given {@link Circle}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param within must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/georadius">Redis Documentation: GEORADIUS</a>
|
||||
*/
|
||||
Mono<List<GeoLocation<M>>> geoRadius(K key, Circle within);
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within the boundaries of a given {@link Circle} applying {@link GeoRadiusCommandArgs}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param within must not be {@literal null}.
|
||||
* @param args must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/georadius">Redis Documentation: GEORADIUS</a>
|
||||
*/
|
||||
Mono<GeoResults<GeoLocation<M>>> geoRadius(K key, Circle within, GeoRadiusCommandArgs args);
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within the circle defined by the {@literal members} coordinates and given
|
||||
* {@literal radius}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @param radius
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/georadiusbymember">Redis Documentation: GEORADIUSBYMEMBER</a>
|
||||
*/
|
||||
Mono<List<GeoLocation<M>>> geoRadiusByMember(K key, M member, double radius);
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within the circle defined by the {@literal members} coordinates and given
|
||||
* {@literal radius} applying {@link Metric}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @param distance must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/georadiusbymember">Redis Documentation: GEORADIUSBYMEMBER</a>
|
||||
*/
|
||||
Mono<List<GeoLocation<M>>> geoRadiusByMember(K key, M member, Distance distance);
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within the circle defined by the {@literal members} coordinates and given
|
||||
* {@literal radius} applying {@link Metric} and {@link GeoRadiusCommandArgs}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @param distance must not be {@literal null}.
|
||||
* @param args must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/georadiusbymember">Redis Documentation: GEORADIUSBYMEMBER</a>
|
||||
*/
|
||||
Mono<GeoResults<GeoLocation<M>>> geoRadiusByMember(K key, M member, Distance distance, GeoRadiusCommandArgs args);
|
||||
|
||||
/**
|
||||
* Remove the {@literal member}s.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param members must not be {@literal null}.
|
||||
* @return Number of elements removed.
|
||||
*/
|
||||
Mono<Long> geoRemove(K key, M... members);
|
||||
|
||||
/**
|
||||
* Removes the given {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
*/
|
||||
Mono<Boolean> delete(K key);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Redis map specific operations working on a hash.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveHashOperations<H, HK, HV> {
|
||||
|
||||
/**
|
||||
* Delete given hash {@code hashKeys} from the hash at {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param hashKeys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<Long> remove(H key, Object... hashKeys);
|
||||
|
||||
/**
|
||||
* Determine if given hash {@code hashKey} exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param hashKey must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<Boolean> hasKey(H key, Object hashKey);
|
||||
|
||||
/**
|
||||
* Get value for given {@code hashKey} from hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param hashKey must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<HV> get(H key, Object hashKey);
|
||||
|
||||
/**
|
||||
* Get values for given {@code hashKeys} from hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param hashKeys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<List<HV>> multiGet(H key, Collection<HK> hashKeys);
|
||||
|
||||
/**
|
||||
* Increment {@code value} of a hash {@code hashKey} by the given {@code delta}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param hashKey must not be {@literal null}.
|
||||
* @param delta
|
||||
* @return
|
||||
*/
|
||||
Mono<Long> increment(H key, HK hashKey, long delta);
|
||||
|
||||
/**
|
||||
* Increment {@code value} of a hash {@code hashKey} by the given {@code delta}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param hashKey must not be {@literal null}.
|
||||
* @param delta
|
||||
* @return
|
||||
*/
|
||||
Mono<Double> increment(H key, HK hashKey, double delta);
|
||||
|
||||
/**
|
||||
* Get key set (fields) of hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<List<HK>> keys(H key);
|
||||
|
||||
/**
|
||||
* Get size of hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<Long> size(H key);
|
||||
|
||||
/**
|
||||
* Set multiple hash fields to multiple values using data provided in {@code m}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param map must not be {@literal null}.
|
||||
*/
|
||||
Mono<Boolean> putAll(H key, Map<? extends HK, ? extends HV> map);
|
||||
|
||||
/**
|
||||
* Set the {@code value} of a hash {@code hashKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param hashKey must not be {@literal null}.
|
||||
* @param value
|
||||
*/
|
||||
Mono<Boolean> put(H key, HK hashKey, HV value);
|
||||
|
||||
/**
|
||||
* Set the {@code value} of a hash {@code hashKey} only if {@code hashKey} does not exist.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param hashKey must not be {@literal null}.
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
Mono<Boolean> putIfAbsent(H key, HK hashKey, HV value);
|
||||
|
||||
/**
|
||||
* Get entry set (values) of hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<List<HV>> values(H key);
|
||||
|
||||
/**
|
||||
* Get entire hash stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<Map<HK, HV>> entries(H key);
|
||||
|
||||
/**
|
||||
* Removes the given {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
*/
|
||||
Mono<Boolean> delete(H key);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Redis cardinality specific operations working on a HyperLogLog multiset.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveHyperLogLogOperations<K, V> {
|
||||
|
||||
/**
|
||||
* Adds the given {@literal values} to the {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return 1 of at least one of the values was added to the key; 0 otherwise.
|
||||
*/
|
||||
Mono<Long> add(K key, V... values);
|
||||
|
||||
/**
|
||||
* Gets the current number of elements within the {@literal key}.
|
||||
*
|
||||
* @param keys must not be {@literal null} or {@literal empty}.
|
||||
* @return
|
||||
*/
|
||||
Mono<Long> size(K... keys);
|
||||
|
||||
/**
|
||||
* Merges all values of given {@literal sourceKeys} into {@literal destination} key.
|
||||
*
|
||||
* @param destination key of HyperLogLog to move source keys into.
|
||||
* @param sourceKeys must not be {@literal null} or {@literal empty}.
|
||||
*/
|
||||
Mono<Boolean> union(K destination, K... sourceKeys);
|
||||
|
||||
/**
|
||||
* Removes the given {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
*/
|
||||
Mono<Boolean> delete(K key);
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Redis list specific operations.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveListOperations<K, V> {
|
||||
|
||||
/**
|
||||
* Get elements between {@code begin} and {@code end} from list at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param start
|
||||
* @param end
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/lrange">Redis Documentation: LRANGE</a>
|
||||
*/
|
||||
Mono<List<V>> range(K key, long start, long end);
|
||||
|
||||
/**
|
||||
* Trim list at {@code key} to elements between {@code start} and {@code end}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param start
|
||||
* @param end
|
||||
* @see <a href="http://redis.io/commands/ltrim">Redis Documentation: LTRIM</a>
|
||||
*/
|
||||
Mono<Boolean> trim(K key, long start, long end);
|
||||
|
||||
/**
|
||||
* Get the size of list stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/llen">Redis Documentation: LLEN</a>
|
||||
*/
|
||||
Mono<Long> size(K key);
|
||||
|
||||
/**
|
||||
* Prepend {@code value} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/lpush">Redis Documentation: LPUSH</a>
|
||||
*/
|
||||
Mono<Long> leftPush(K key, V value);
|
||||
|
||||
/**
|
||||
* Prepend {@code values} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/lpush">Redis Documentation: LPUSH</a>
|
||||
*/
|
||||
Mono<Long> leftPushAll(K key, V... values);
|
||||
|
||||
/**
|
||||
* Prepend {@code values} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.5
|
||||
* @see <a href="http://redis.io/commands/lpush">Redis Documentation: LPUSH</a>
|
||||
*/
|
||||
Mono<Long> leftPushAll(K key, Collection<V> values);
|
||||
|
||||
/**
|
||||
* Prepend {@code values} to {@code key} only if the list exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/lpushx">Redis Documentation: LPUSHX</a>
|
||||
*/
|
||||
Mono<Long> leftPushIfPresent(K key, V value);
|
||||
|
||||
/**
|
||||
* Prepend {@code values} to {@code key} before {@code value}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/lpush">Redis Documentation: LPUSH</a>
|
||||
*/
|
||||
Mono<Long> leftPush(K key, V pivot, V value);
|
||||
|
||||
/**
|
||||
* Append {@code value} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/rpush">Redis Documentation: RPUSH</a>
|
||||
*/
|
||||
Mono<Long> rightPush(K key, V value);
|
||||
|
||||
/**
|
||||
* Append {@code values} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/rpush">Redis Documentation: RPUSH</a>
|
||||
*/
|
||||
Mono<Long> rightPushAll(K key, V... values);
|
||||
|
||||
/**
|
||||
* Append {@code values} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values
|
||||
* @return
|
||||
* @since 1.5
|
||||
* @see <a href="http://redis.io/commands/rpush">Redis Documentation: RPUSH</a>
|
||||
*/
|
||||
Mono<Long> rightPushAll(K key, Collection<V> values);
|
||||
|
||||
/**
|
||||
* Append {@code values} to {@code key} only if the list exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/rpushx">Redis Documentation: RPUSHX</a>
|
||||
*/
|
||||
Mono<Long> rightPushIfPresent(K key, V value);
|
||||
|
||||
/**
|
||||
* Append {@code values} to {@code key} before {@code value}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/lpush">Redis Documentation: RPUSH</a>
|
||||
*/
|
||||
Mono<Long> rightPush(K key, V pivot, V value);
|
||||
|
||||
/**
|
||||
* Set the {@code value} list element at {@code index}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param index
|
||||
* @param value
|
||||
* @see <a href="http://redis.io/commands/lset">Redis Documentation: LSET</a>
|
||||
*/
|
||||
Mono<Boolean> set(K key, long index, V value);
|
||||
|
||||
/**
|
||||
* Removes the first {@code count} occurrences of {@code value} from the list stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param count
|
||||
* @param value
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/lrem">Redis Documentation: LREM</a>
|
||||
*/
|
||||
Mono<Long> remove(K key, long count, Object value);
|
||||
|
||||
/**
|
||||
* Get element at {@code index} form list at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param index
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/lindex">Redis Documentation: LINDEX</a>
|
||||
*/
|
||||
Mono<V> index(K key, long index);
|
||||
|
||||
/**
|
||||
* Removes and returns first element in list stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/lpop">Redis Documentation: LPOP</a>
|
||||
*/
|
||||
Mono<V> leftPop(K key);
|
||||
|
||||
/**
|
||||
* Removes and returns first element from lists stored at {@code key}. <br>
|
||||
* <b>Results return once an element available or {@code timeout} reached.</b>
|
||||
*
|
||||
* @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.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/blpop">Redis Documentation: BLPOP</a>
|
||||
*/
|
||||
Mono<V> leftPop(K key, Duration timeout);
|
||||
|
||||
/**
|
||||
* Removes and returns last element in list stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/rpop">Redis Documentation: RPOP</a>
|
||||
*/
|
||||
Mono<V> rightPop(K key);
|
||||
|
||||
/**
|
||||
* Removes and returns last element from lists stored at {@code key}. <br>
|
||||
* <b>Results return once an element available or {@code timeout} reached.</b>
|
||||
*
|
||||
* @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.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/brpop">Redis Documentation: BRPOP</a>
|
||||
*/
|
||||
Mono<V> rightPop(K key, Duration timeout);
|
||||
|
||||
/**
|
||||
* Remove the last element from list at {@code sourceKey}, append it to {@code destinationKey} and return its value.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/rpoplpush">Redis Documentation: RPOPLPUSH</a>
|
||||
*/
|
||||
Mono<V> rightPopAndLeftPush(K sourceKey, K destinationKey);
|
||||
|
||||
/**
|
||||
* Remove the last element from list at {@code srcKey}, append it to {@code dstKey} and return its value.<br>
|
||||
* <b>Results return once an element available or {@code timeout} reached.</b>
|
||||
*
|
||||
* @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.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/brpoplpush">Redis Documentation: BRPOPLPUSH</a>
|
||||
*/
|
||||
Mono<V> rightPopAndLeftPush(K sourceKey, K destinationKey, Duration timeout);
|
||||
|
||||
/**
|
||||
* Removes the given {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
*/
|
||||
Mono<Boolean> delete(K key);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
|
||||
/**
|
||||
* Generic callback interface for code that operates on a low-level {@link ReactiveRedisConnection}. Allows to execute
|
||||
* any number of operations on a single {@link ReactiveRedisConnection}, using any type and number of commands.
|
||||
* <p>
|
||||
* This is particularly useful for delegating to existing data access code that expects a
|
||||
* {@link ReactiveRedisConnection} to work on. For newly written code, it is strongly recommended to use
|
||||
* {@link ReactiveRedisOperations}'s more specific operations.
|
||||
*
|
||||
* @param <T>
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
* @see ReactiveRedisOperations#execute(ReactiveRedisCallback)
|
||||
*/
|
||||
public interface ReactiveRedisCallback<T> {
|
||||
|
||||
/**
|
||||
* Gets called by {@link ReactiveRedisTemplate#execute(ReactiveRedisCallback)} with an active Redis connection. Does
|
||||
* not need to care about activating or closing the {@link ReactiveRedisConnection}.
|
||||
* <p>
|
||||
* Allows for returning a result object created within the callback, i.e. a domain object or a collection of domain
|
||||
* objects.
|
||||
*
|
||||
* @param connection active Redis connection.
|
||||
* @return a result object publisher
|
||||
* @throws DataAccessException in case of custom exceptions
|
||||
*/
|
||||
Publisher<T> doInRedis(ReactiveRedisConnection connection) throws DataAccessException;
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext;
|
||||
|
||||
/**
|
||||
* Interface that specified a basic set of Redis operations, implemented by {@link ReactiveRedisTemplate}. Not often
|
||||
* used but a useful option for extensibility and testability (as it can be easily mocked or stubbed).
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveRedisOperations<K, V> {
|
||||
|
||||
/**
|
||||
* Executes the given action within a Redis connection. Application exceptions thrown by the action object get
|
||||
* propagated to the caller (can only be unchecked) whenever possible. Redis exceptions are transformed into
|
||||
* appropriate DAO ones. Allows for returning a result object, that is a domain object or a collection of domain
|
||||
* objects. Performs automatic serialization/deserialization for the given objects to and from binary data suitable
|
||||
* for the Redis storage. Note: Callback code is not supposed to handle transactions itself! Use an appropriate
|
||||
* transaction manager. Generally, callback code must not touch any Connection lifecycle methods, like close, to let
|
||||
* the template do its work.
|
||||
*
|
||||
* @param <T> return type
|
||||
* @param action callback object that specifies the Redis action
|
||||
* @return a result object returned by the action or <tt>null</tt>
|
||||
*/
|
||||
<T> Flux<T> execute(ReactiveRedisCallback<T> action);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with Redis Keys
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determine if given {@code key} exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/exists">Redis Documentation: EXISTS</a>
|
||||
*/
|
||||
Mono<Boolean> hasKey(K key);
|
||||
|
||||
/**
|
||||
* Determine the type stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/type">Redis Documentation: TYPE</a>
|
||||
*/
|
||||
Mono<DataType> type(K key);
|
||||
|
||||
/**
|
||||
* Find all keys matching the given {@code pattern}.
|
||||
*
|
||||
* @param pattern must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/keys">Redis Documentation: KEYS</a>
|
||||
*/
|
||||
Flux<K> keys(K pattern);
|
||||
|
||||
/**
|
||||
* Return a random key from the keyspace.
|
||||
*
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/randomkey">Redis Documentation: RANDOMKEY</a>
|
||||
*/
|
||||
Mono<K> randomKey();
|
||||
|
||||
/**
|
||||
* Rename key {@code oldKey} to {@code newKey}.
|
||||
*
|
||||
* @param oldKey must not be {@literal null}.
|
||||
* @param newKey must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/rename">Redis Documentation: RENAME</a>
|
||||
*/
|
||||
Mono<Boolean> rename(K oldKey, K newKey);
|
||||
|
||||
/**
|
||||
* Rename key {@code oleName} to {@code newKey} only if {@code newKey} does not exist.
|
||||
*
|
||||
* @param oldKey must not be {@literal null}.
|
||||
* @param newKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/renamenx">Redis Documentation: RENAMENX</a>
|
||||
*/
|
||||
Mono<Boolean> renameIfAbsent(K oldKey, K newKey);
|
||||
|
||||
/**
|
||||
* Delete given {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return The number of keys that were removed.
|
||||
* @see <a href="http://redis.io/commands/del">Redis Documentation: DEL</a>
|
||||
*/
|
||||
Mono<Long> delete(K... key);
|
||||
|
||||
/**
|
||||
* Delete given {@code keys}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return The number of keys that were removed.
|
||||
* @see <a href="http://redis.io/commands/del">Redis Documentation: DEL</a>
|
||||
*/
|
||||
Mono<Long> delete(Publisher<K> keys);
|
||||
|
||||
/**
|
||||
* Set time to live for given {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<Boolean> expire(K key, Duration timeout);
|
||||
|
||||
/**
|
||||
* Set the expiration for given {@code key} as a {@literal expireAt} timestamp.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param expireAt must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<Boolean> expireAt(K key, Instant expireAt);
|
||||
|
||||
/**
|
||||
* Remove the expiration from given {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/persist">Redis Documentation: PERSIST</a>
|
||||
*/
|
||||
Mono<Boolean> persist(K key);
|
||||
|
||||
/**
|
||||
* Move given {@code key} to database with {@code index}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param dbIndex
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/move">Redis Documentation: MOVE</a>
|
||||
*/
|
||||
Mono<Boolean> move(K key, int dbIndex);
|
||||
|
||||
/**
|
||||
* Get the time to live for {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return the {@link Duration} of the associated key. {@link Duration#ZERO} if no timeout associated or empty
|
||||
* {@link Mono} if the key does not exist.
|
||||
* @see <a href="http://redis.io/commands/pttl">Redis Documentation: PTTL</a>
|
||||
*/
|
||||
Mono<Duration> getExpire(K key);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods to obtain specific operations interface objects.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// operation types
|
||||
|
||||
/**
|
||||
* Returns the operations performed on simple values (or Strings in Redis terminology).
|
||||
*
|
||||
* @return value operations
|
||||
*/
|
||||
ReactiveValueOperations<K, V> opsForValue();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on simple values (or Strings in Redis terminology) given a
|
||||
* {@link ReactiveSerializationContext}.
|
||||
*
|
||||
* @param serializationContext serializers to be used with the returned operations, must not be {@literal null}.
|
||||
* @return value operations.
|
||||
*/
|
||||
<K, V> ReactiveValueOperations<K, V> opsForValue(ReactiveSerializationContext<K, V> serializationContext);
|
||||
|
||||
/**
|
||||
* Returns the operations performed on list values.
|
||||
*
|
||||
* @return list operations.
|
||||
*/
|
||||
ReactiveListOperations<K, V> opsForList();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on list values given a {@link ReactiveSerializationContext}.
|
||||
*
|
||||
* @param serializationContext serializers to be used with the returned operations, must not be {@literal null}.
|
||||
* @return list operations.
|
||||
*/
|
||||
<K, V> ReactiveListOperations<K, V> opsForList(ReactiveSerializationContext<K, V> serializationContext);
|
||||
|
||||
/**
|
||||
* Returns the operations performed on set values.
|
||||
*
|
||||
* @return set operations.
|
||||
*/
|
||||
ReactiveSetOperations<K, V> opsForSet();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on set values given a {@link ReactiveSerializationContext}.
|
||||
*
|
||||
* @param serializationContext serializers to be used with the returned operations, must not be {@literal null}.
|
||||
* @return set operations.
|
||||
*/
|
||||
<K, V> ReactiveSetOperations<K, V> opsForSet(ReactiveSerializationContext<K, V> serializationContext);
|
||||
|
||||
/**
|
||||
* Returns the operations performed on zset values (also known as sorted sets).
|
||||
*
|
||||
* @return zset operations.
|
||||
*/
|
||||
ReactiveZSetOperations<K, V> opsForZSet();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on zset values (also known as sorted sets) given a
|
||||
* {@link ReactiveSerializationContext}.
|
||||
*
|
||||
* @param serializationContext serializers to be used with the returned operations, must not be {@literal null}.
|
||||
* @return zset operations.
|
||||
*/
|
||||
<K, V> ReactiveZSetOperations<K, V> opsForZSet(ReactiveSerializationContext<K, V> serializationContext);
|
||||
|
||||
/**
|
||||
* Returns the operations performed on multisets using HyperLogLog.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
ReactiveHyperLogLogOperations<K, V> opsForHyperLogLog();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on multisets using HyperLogLog given a {@link ReactiveSerializationContext}.
|
||||
*
|
||||
* @param serializationContext serializers to be used with the returned operations, must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
<K, V> ReactiveHyperLogLogOperations<K, V> opsForHyperLogLog(ReactiveSerializationContext<K, V> serializationContext);
|
||||
|
||||
/**
|
||||
* Returns the operations performed on hash values.
|
||||
*
|
||||
* @param <HK> hash key (or field) type.
|
||||
* @param <HV> hash value type.
|
||||
* @return hash operations.
|
||||
*/
|
||||
<HK, HV> ReactiveHashOperations<K, HK, HV> opsForHash();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on hash values given a {@link ReactiveSerializationContext}.
|
||||
*
|
||||
* @param serializationContext serializers to be used with the returned operations, must not be {@literal null}.
|
||||
* @param <HK> hash key (or field) type.
|
||||
* @param <HV> hash value type.
|
||||
* @return hash operations.
|
||||
*/
|
||||
<K, HK, HV> ReactiveHashOperations<K, HK, HV> opsForHash(ReactiveSerializationContext<K, ?> serializationContext);
|
||||
|
||||
/**
|
||||
* Returns geospatial specific operations interface.
|
||||
*
|
||||
* @return geospatial specific operations.
|
||||
*/
|
||||
ReactiveGeoOperations<K, V> opsForGeo();
|
||||
|
||||
/**
|
||||
* Returns geospatial specific operations interface.
|
||||
*
|
||||
* @param serializationContext serializers to be used with the returned operations, must not be {@literal null}.
|
||||
* @return geospatial specific operations.
|
||||
*/
|
||||
<K, V> ReactiveGeoOperations<K, V> opsForGeo(ReactiveSerializationContext<K, V> serializationContext);
|
||||
|
||||
/**
|
||||
* @return the {@link ReactiveSerializationContext}.
|
||||
*/
|
||||
ReactiveSerializationContext<K, V> getSerializationContext();
|
||||
}
|
||||
@@ -0,0 +1,957 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Central abstraction for reactive Redis data access.
|
||||
* <p/>
|
||||
* Performs automatic serialization/deserialization between the given objects and the underlying binary data in the
|
||||
* Redis store. By default, it uses Java serialization for its objects (through {@link JdkSerializationRedisSerializer}
|
||||
* ).
|
||||
* <p/>
|
||||
* Once configured, this class is thread-safe.
|
||||
* <p/>
|
||||
* Note that while the template is generified, it is up to the serializers/deserializers to properly convert the given
|
||||
* Objects to and from binary data.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
* @param <K> the Redis key type against which the template works (usually a String)
|
||||
* @param <V> the Redis value type against which the template works
|
||||
*/
|
||||
public class ReactiveRedisTemplate<K, V>
|
||||
implements BeanClassLoaderAware, InitializingBean, ReactiveRedisOperations<K, V> {
|
||||
|
||||
private ReactiveRedisConnectionFactory connectionFactory;
|
||||
private boolean exposeConnection = true;
|
||||
private boolean initialized = false;
|
||||
private boolean enableDefaultSerializer = true;
|
||||
private RedisSerializer<?> defaultSerializer;
|
||||
private ClassLoader classLoader;
|
||||
private ReactiveSerializationContextSupport<K, V> serializationContext = new MutableReactiveSerializationContext<>();
|
||||
|
||||
// cache singleton objects (where possible)
|
||||
private ReactiveValueOperations<K, V> valueOps;
|
||||
private ReactiveListOperations<K, V> listOps;
|
||||
private ReactiveSetOperations<K, V> setOps;
|
||||
private ReactiveZSetOperations<K, V> zSetOps;
|
||||
private ReactiveHyperLogLogOperations<K, V> hyperLogLogOps;
|
||||
private ReactiveGeoOperations<K, V> geoOps;
|
||||
|
||||
/**
|
||||
* Construct a new {@link ReactiveRedisTemplate} instance.
|
||||
*/
|
||||
public ReactiveRedisTemplate() {}
|
||||
|
||||
/**
|
||||
* Returns the connectionFactory.
|
||||
*
|
||||
* @return Returns the connectionFactory
|
||||
*/
|
||||
public ReactiveRedisConnectionFactory getConnectionFactory() {
|
||||
return connectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection factory.
|
||||
*
|
||||
* @param connectionFactory The connectionFactory to set.
|
||||
*/
|
||||
public void setConnectionFactory(ReactiveRedisConnectionFactory connectionFactory) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param enableDefaultSerializer Whether or not the default serializer should be used. If not, any serializers not
|
||||
* explicilty set will remain null and values will not be serialized or deserialized.
|
||||
*/
|
||||
public void setEnableDefaultSerializer(boolean enableDefaultSerializer) {
|
||||
this.enableDefaultSerializer = enableDefaultSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default serializer used by this template.
|
||||
*
|
||||
* @return template default serializer
|
||||
*/
|
||||
public RedisSerializer<?> getDefaultSerializer() {
|
||||
return defaultSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default serializer to use for this template. All serializers (expect the
|
||||
* {@link #setStringSerializer(RedisSerializer)}) are initialized to this value unless explicitly set. Defaults to
|
||||
* {@link JdkSerializationRedisSerializer}.
|
||||
*
|
||||
* @param serializer default serializer to use
|
||||
*/
|
||||
public void setDefaultSerializer(RedisSerializer<?> serializer) {
|
||||
this.defaultSerializer = serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key serializer used by this template.
|
||||
*
|
||||
* @return the key serializer used by this template.
|
||||
*/
|
||||
public RedisSerializer<K> getKeySerializer() {
|
||||
return serializationContext.getKeySerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key serializer to be used by this template. Defaults to {@link #getDefaultSerializer()}.
|
||||
*
|
||||
* @param serializer the key serializer to be used by this template.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setKeySerializer(RedisSerializer<?> serializer) {
|
||||
getMutableSerializationContext().setKeySerializer((RedisSerializer) serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value serializer used by this template.
|
||||
*
|
||||
* @return the value serializer used by this template.
|
||||
*/
|
||||
public RedisSerializer<V> getValueSerializer() {
|
||||
return serializationContext.getValueSerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value serializer to be used by this template. Defaults to {@link #getDefaultSerializer()}.
|
||||
*
|
||||
* @param serializer the value serializer to be used by this template.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setValueSerializer(RedisSerializer<?> serializer) {
|
||||
getMutableSerializationContext().setValueSerializer((RedisSerializer) serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hashKeySerializer.
|
||||
*
|
||||
* @return Returns the hashKeySerializer
|
||||
*/
|
||||
public RedisSerializer<?> getHashKeySerializer() {
|
||||
return serializationContext.getHashKeySerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hash key (or field) serializer to be used by this template. Defaults to {@link #getDefaultSerializer()}.
|
||||
*
|
||||
* @param hashKeySerializer The hashKeySerializer to set.
|
||||
*/
|
||||
public void setHashKeySerializer(RedisSerializer<?> hashKeySerializer) {
|
||||
getMutableSerializationContext().setHashKeySerializer(hashKeySerializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hashValueSerializer.
|
||||
*
|
||||
* @return Returns the hashValueSerializer
|
||||
*/
|
||||
public RedisSerializer<?> getHashValueSerializer() {
|
||||
return serializationContext.getHashValueSerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hash value serializer to be used by this template. Defaults to {@link #getDefaultSerializer()}.
|
||||
*
|
||||
* @param hashValueSerializer The hashValueSerializer to set.
|
||||
*/
|
||||
public void setHashValueSerializer(RedisSerializer<?> hashValueSerializer) {
|
||||
getMutableSerializationContext().setHashValueSerializer(hashValueSerializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stringSerializer.
|
||||
*
|
||||
* @return Returns the stringSerializer
|
||||
*/
|
||||
public RedisSerializer<String> getStringSerializer() {
|
||||
return serializationContext.getStringSerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the string value serializer to be used by this template (when the arguments or return types are always
|
||||
* strings). Defaults to {@link StringRedisSerializer}.
|
||||
*
|
||||
* @param stringSerializer The stringValueSerializer to set.
|
||||
* @see ValueOperations#get(Object, long, long)
|
||||
*/
|
||||
public void setStringSerializer(RedisSerializer<String> stringSerializer) {
|
||||
getMutableSerializationContext().setStringSerializer(stringSerializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 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}.
|
||||
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader
|
||||
*/
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the properties and creates and sets an immutable {@link ReactiveSerializationContext}. Serializers
|
||||
* cannot be changed after properties are initialized.
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
boolean defaultUsed = false;
|
||||
|
||||
if (this.defaultSerializer == null) {
|
||||
|
||||
this.defaultSerializer = new JdkSerializationRedisSerializer(
|
||||
this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
|
||||
}
|
||||
|
||||
if (this.enableDefaultSerializer) {
|
||||
|
||||
if (this.serializationContext.getKeySerializer() == null) {
|
||||
setKeySerializer(this.defaultSerializer);
|
||||
defaultUsed = true;
|
||||
}
|
||||
|
||||
if (this.serializationContext.getValueSerializer() == null) {
|
||||
setValueSerializer(this.defaultSerializer);
|
||||
defaultUsed = true;
|
||||
}
|
||||
|
||||
if (this.serializationContext.getHashKeySerializer() == null) {
|
||||
setHashKeySerializer(this.defaultSerializer);
|
||||
defaultUsed = true;
|
||||
}
|
||||
|
||||
if (this.serializationContext.getHashValueSerializer() == null) {
|
||||
setHashValueSerializer(this.defaultSerializer);
|
||||
defaultUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.enableDefaultSerializer && defaultUsed) {
|
||||
Assert.notNull(this.defaultSerializer, "Default serializer is null and not all serializers initialized");
|
||||
}
|
||||
|
||||
this.serializationContext = new ImmutableReactiveSerializationContext<K, V>(serializationContext);
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForValue()
|
||||
*/
|
||||
@Override
|
||||
public ReactiveValueOperations<K, V> opsForValue() {
|
||||
|
||||
if (valueOps == null) {
|
||||
valueOps = opsForValue(serializationContext);
|
||||
}
|
||||
|
||||
return valueOps;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForValue(org.springframework.data.redis.serializer.ReactiveSerializationContext)
|
||||
*/
|
||||
@Override
|
||||
public <K1, V1> ReactiveValueOperations<K1, V1> opsForValue(
|
||||
ReactiveSerializationContext<K1, V1> serializationContext) {
|
||||
return new DefaultReactiveValueOperations<>(this, serializationContext);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForList()
|
||||
*/
|
||||
@Override
|
||||
public ReactiveListOperations<K, V> opsForList() {
|
||||
|
||||
if (listOps == null) {
|
||||
listOps = opsForList(serializationContext);
|
||||
}
|
||||
|
||||
return listOps;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForList(org.springframework.data.redis.serializer.ReactiveSerializationContext)
|
||||
*/
|
||||
@Override
|
||||
public <K1, V1> ReactiveListOperations<K1, V1> opsForList(ReactiveSerializationContext<K1, V1> serializationContext) {
|
||||
return new DefaultReactiveListOperations<>(this, serializationContext);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForSet()
|
||||
*/
|
||||
public ReactiveSetOperations<K, V> opsForSet() {
|
||||
|
||||
if (setOps == null) {
|
||||
setOps = opsForSet(serializationContext);
|
||||
}
|
||||
|
||||
return setOps;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForSet(org.springframework.data.redis.serializer.ReactiveSerializationContext)
|
||||
*/
|
||||
@Override
|
||||
public <K1, V1> ReactiveSetOperations<K1, V1> opsForSet(ReactiveSerializationContext<K1, V1> serializationContext) {
|
||||
return new DefaultReactiveSetOperations<>(this, serializationContext);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForZSet()
|
||||
*/
|
||||
public ReactiveZSetOperations<K, V> opsForZSet() {
|
||||
|
||||
if (zSetOps == null) {
|
||||
zSetOps = opsForZSet(serializationContext);
|
||||
}
|
||||
|
||||
return zSetOps;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForZSet(org.springframework.data.redis.serializer.ReactiveSerializationContext)
|
||||
*/
|
||||
@Override
|
||||
public <K1, V1> ReactiveZSetOperations<K1, V1> opsForZSet(ReactiveSerializationContext<K1, V1> serializationContext) {
|
||||
return new DefaultReactiveZSetOperations<>(this, serializationContext);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForHyperLogLog()
|
||||
*/
|
||||
@Override
|
||||
public ReactiveHyperLogLogOperations<K, V> opsForHyperLogLog() {
|
||||
|
||||
if (hyperLogLogOps == null) {
|
||||
hyperLogLogOps = opsForHyperLogLog(serializationContext);
|
||||
}
|
||||
|
||||
return hyperLogLogOps;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForHyperLogLog(org.springframework.data.redis.serializer.ReactiveSerializationContext)
|
||||
*/
|
||||
@Override
|
||||
public <K1, V1> ReactiveHyperLogLogOperations<K1, V1> opsForHyperLogLog(
|
||||
ReactiveSerializationContext<K1, V1> serializationContext) {
|
||||
return new DefaultReactiveHyperLogLogOperations<>(this, serializationContext);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForHash()
|
||||
*/
|
||||
@Override
|
||||
public <HK, HV> ReactiveHashOperations<K, HK, HV> opsForHash() {
|
||||
return opsForHash(serializationContext);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForHash(org.springframework.data.redis.serializer.ReactiveSerializationContext)
|
||||
*/
|
||||
@Override
|
||||
public <K1, HK, HV> ReactiveHashOperations<K1, HK, HV> opsForHash(
|
||||
ReactiveSerializationContext<K1, ?> serializationContext) {
|
||||
return new DefaultReactiveHashOperations<>(this, serializationContext);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForGeo()
|
||||
*/
|
||||
@Override
|
||||
public ReactiveGeoOperations<K, V> opsForGeo() {
|
||||
|
||||
if (geoOps == null) {
|
||||
geoOps = opsForGeo(serializationContext);
|
||||
}
|
||||
|
||||
return geoOps;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#opsForGeo(org.springframework.data.redis.serializer.ReactiveSerializationContext)
|
||||
*/
|
||||
@Override
|
||||
public <K1, V1> ReactiveGeoOperations<K1, V1> opsForGeo(ReactiveSerializationContext<K1, V1> serializationContext) {
|
||||
return new DefaultReactiveGeoOperations<>(this, serializationContext);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Execution methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public <T> Flux<T> execute(ReactiveRedisCallback<T> action) {
|
||||
return execute(action, exposeConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given action object within a connection that can be exposed or not. Additionally, the connection can
|
||||
* be pipelined. Note the results of the pipeline are discarded (making it suitable for write-only scenarios).
|
||||
*
|
||||
* @param <T> return type
|
||||
* @param action callback object to execute
|
||||
* @param exposeConnection whether to enforce exposure of the native Redis Connection to callback code
|
||||
* @return object returned by the action
|
||||
*/
|
||||
public <T> Flux<T> execute(ReactiveRedisCallback<T> action, boolean exposeConnection) {
|
||||
|
||||
Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
|
||||
Assert.notNull(action, "Callback object must not be null");
|
||||
|
||||
ReactiveRedisConnectionFactory factory = getConnectionFactory();
|
||||
ReactiveRedisConnection conn = factory.getReactiveConnection();
|
||||
|
||||
try {
|
||||
|
||||
ReactiveRedisConnection connToUse = preProcessConnection(conn, false);
|
||||
|
||||
ReactiveRedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));
|
||||
Publisher<T> result = action.doInRedis(connToExpose);
|
||||
|
||||
return Flux.from(postProcessResult(result, connToUse, false));
|
||||
} finally {
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reusable Flux for a {@link ReactiveRedisCallback}. Callback is executed within a connection context. The
|
||||
* connection is released outside the callback.
|
||||
*
|
||||
* @param callback must not be {@literal null}
|
||||
* @return a {@link Flux} wrapping the {@link ReactiveRedisCallback}.
|
||||
*/
|
||||
public <T> Flux<T> createFlux(ReactiveRedisCallback<T> callback) {
|
||||
|
||||
Assert.notNull(callback, "ReactiveRedisCallback must not be null!");
|
||||
|
||||
return Flux.defer(() -> doInConnection(callback, exposeConnection));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reusable Mono for a {@link ReactiveRedisCallback}. Callback is executed within a connection context. The
|
||||
* connection is released outside the callback.
|
||||
*
|
||||
* @param callback must not be {@literal null}
|
||||
* @return a {@link Mono} wrapping the {@link ReactiveRedisCallback}.
|
||||
*/
|
||||
public <T> Mono<T> createMono(final ReactiveRedisCallback<T> callback) {
|
||||
|
||||
Assert.notNull(callback, "ReactiveRedisCallback must not be null!");
|
||||
|
||||
return Mono.defer(() -> Mono.from(doInConnection(callback, exposeConnection)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given action object within a connection that can be exposed or not. Additionally, the connection can
|
||||
* be pipelined. Note the results of the pipeline are discarded (making it suitable for write-only scenarios).
|
||||
*
|
||||
* @param <T> return type
|
||||
* @param action callback object to execute
|
||||
* @param exposeConnection whether to enforce exposure of the native Redis Connection to callback code
|
||||
* @return object returned by the action
|
||||
*/
|
||||
private <T> Publisher<T> doInConnection(ReactiveRedisCallback<T> action, boolean exposeConnection) {
|
||||
|
||||
Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
|
||||
Assert.notNull(action, "Callback object must not be null");
|
||||
|
||||
ReactiveRedisConnectionFactory factory = getConnectionFactory();
|
||||
ReactiveRedisConnection conn = factory.getReactiveConnection();
|
||||
|
||||
ReactiveRedisConnection connToUse = preProcessConnection(conn, false);
|
||||
|
||||
ReactiveRedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));
|
||||
Publisher<T> result = action.doInRedis(connToExpose);
|
||||
|
||||
return Flux.from(postProcessResult(result, connToUse, false)).doAfterTerminate(conn::close);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with Redis keys
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#hasKey(java.lang.Object)
|
||||
*/
|
||||
public Mono<Boolean> hasKey(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.keyCommands().exists(rawKey(key)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#type(java.lang.Object)
|
||||
*/
|
||||
public Mono<DataType> type(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.keyCommands().type(rawKey(key)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#keys(java.lang.Object)
|
||||
*/
|
||||
public Flux<K> keys(K pattern) {
|
||||
|
||||
Assert.notNull(pattern, "Pattern must not be null!");
|
||||
|
||||
return createFlux(connection -> connection.keyCommands().keys(rawKey(pattern))) //
|
||||
.flatMap(Flux::fromIterable) //
|
||||
.map(this::readKey);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#randomKey()
|
||||
*/
|
||||
public Mono<K> randomKey() {
|
||||
return createMono(connection -> connection.keyCommands().randomKey()).map(this::readKey);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#rename(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public Mono<Boolean> rename(K oldKey, K newKey) {
|
||||
|
||||
Assert.notNull(oldKey, "Old key must not be null!");
|
||||
Assert.notNull(newKey, "New Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.keyCommands().rename(rawKey(oldKey), rawKey(newKey)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#renameIfAbsent(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public Mono<Boolean> renameIfAbsent(K oldKey, K newKey) {
|
||||
|
||||
Assert.notNull(oldKey, "Old key must not be null!");
|
||||
Assert.notNull(newKey, "New Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.keyCommands().renameNX(rawKey(oldKey), rawKey(newKey)));
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#delete(java.lang.Object[])
|
||||
*/
|
||||
@SafeVarargs
|
||||
public final Mono<Long> delete(K... keys) {
|
||||
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
Assert.notEmpty(keys, "Keys must not be empty!");
|
||||
Assert.noNullElements(keys, "Keys must not contain null elements!");
|
||||
|
||||
if (keys.length == 1) {
|
||||
return createMono(connection -> connection.keyCommands().del(rawKey(keys[0])));
|
||||
}
|
||||
|
||||
Mono<List<ByteBuffer>> listOfKeys = Flux.fromArray(keys).map(this::rawKey).collectList();
|
||||
return createMono(connection -> listOfKeys.flatMap(rawKeys -> connection.keyCommands().mDel(rawKeys)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#delete(org.reactivestreams.Publisher)
|
||||
*/
|
||||
public Mono<Long> delete(Publisher<K> keys) {
|
||||
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
|
||||
return createMono(connection -> connection.keyCommands() //
|
||||
.del(Flux.from(keys).map(this::rawKey).map(KeyCommand::new)) //
|
||||
.map(CommandResponse::getOutput));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#expire(java.lang.Object, java.time.Duration)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> expire(K key, Duration timeout) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(timeout, "Timeout must not be null!");
|
||||
|
||||
if (timeout.getNano() == 0) {
|
||||
return createMono(connection -> connection.keyCommands() //
|
||||
.expire(rawKey(key), timeout));
|
||||
}
|
||||
|
||||
return createMono(connection -> connection.keyCommands().pExpire(rawKey(key), timeout));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#expireAt(java.lang.Object, java.time.Instant)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> expireAt(K key, Instant expireAt) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(expireAt, "Expire at must not be null!");
|
||||
|
||||
if (expireAt.getNano() == 0) {
|
||||
return createMono(connection -> connection.keyCommands() //
|
||||
.expireAt(rawKey(key), expireAt));
|
||||
}
|
||||
|
||||
return createMono(connection -> connection.keyCommands().pExpireAt(rawKey(key), expireAt));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#persist(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> persist(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.keyCommands().persist(rawKey(key)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#getExpire(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Duration> getExpire(K key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.keyCommands().pTtl(rawKey(key)).flatMap(expiry -> {
|
||||
|
||||
if (expiry == -1) {
|
||||
return Mono.just(Duration.ZERO);
|
||||
}
|
||||
|
||||
if (expiry == -2) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return Mono.just(Duration.ofMillis(expiry));
|
||||
}));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#move(java.lang.Object, int)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> move(K key, int dbIndex) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> connection.keyCommands().move(rawKey(key), dbIndex));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Implementation hooks and helper methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Processes the connection (before any settings are executed on it). Default implementation returns the connection as
|
||||
* is.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
* @param existingConnection
|
||||
*/
|
||||
protected ReactiveRedisConnection preProcessConnection(ReactiveRedisConnection connection,
|
||||
boolean existingConnection) {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the result before returning the {@link Publisher}. Default implementation returns the result as is.
|
||||
*
|
||||
* @param result must not be {@literal null}.
|
||||
* @param connection must not be {@literal null}.
|
||||
* @param existingConnection
|
||||
* @return
|
||||
*/
|
||||
protected <T> Publisher<T> postProcessResult(Publisher<T> result, ReactiveRedisConnection connection,
|
||||
boolean existingConnection) {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected ReactiveRedisConnection createRedisConnectionProxy(ReactiveRedisConnection reactiveRedisConnection) {
|
||||
|
||||
Class<?>[] ifcs = ClassUtils.getAllInterfacesForClass(reactiveRedisConnection.getClass(),
|
||||
getClass().getClassLoader());
|
||||
return (ReactiveRedisConnection) Proxy.newProxyInstance(reactiveRedisConnection.getClass().getClassLoader(), ifcs,
|
||||
new CloseSuppressingInvocationHandler(reactiveRedisConnection));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#serialization()
|
||||
*/
|
||||
@Override
|
||||
public ReactiveSerializationContext<K, V> getSerializationContext() {
|
||||
return serializationContext;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private MutableReactiveSerializationContext<K, V> getMutableSerializationContext() {
|
||||
|
||||
Assert.state(serializationContext instanceof MutableReactiveSerializationContext,
|
||||
() -> String.format("Client configuration must be instance of MutableReactiveSerializationContext but is %s",
|
||||
ClassUtils.getShortName(serializationContext.getClass())));
|
||||
|
||||
return (MutableReactiveSerializationContext) serializationContext;
|
||||
}
|
||||
|
||||
private ByteBuffer rawKey(K key) {
|
||||
return getSerializationContext().key().getWriter().write(key);
|
||||
}
|
||||
|
||||
private K readKey(ByteBuffer buffer) {
|
||||
return getSerializationContext().key().getReader().read(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static abstract class ReactiveSerializationContextSupport<K, V> implements ReactiveSerializationContext<K, V> {
|
||||
|
||||
@Override
|
||||
public abstract SerializationTuple<K> key();
|
||||
|
||||
@Override
|
||||
public abstract SerializationTuple<V> value();
|
||||
|
||||
@Override
|
||||
public abstract SerializationTuple<String> string();
|
||||
|
||||
@Override
|
||||
public abstract <HK> SerializationTuple<HK> hashKey();
|
||||
|
||||
@Override
|
||||
public abstract <HV> SerializationTuple<HV> hashValue();
|
||||
|
||||
public abstract RedisSerializer<K> getKeySerializer();
|
||||
|
||||
public abstract RedisSerializer<V> getValueSerializer();
|
||||
|
||||
public abstract RedisSerializer<?> getHashKeySerializer();
|
||||
|
||||
public abstract RedisSerializer<?> getHashValueSerializer();
|
||||
|
||||
public abstract RedisSerializer<String> getStringSerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class ImmutableReactiveSerializationContext<K, V> extends ReactiveSerializationContextSupport<K, V> {
|
||||
|
||||
private RedisSerializer<K> keySerializer;
|
||||
private final SerializationTuple<K> keyTuple;
|
||||
|
||||
private RedisSerializer<V> valueSerializer;
|
||||
private final SerializationTuple<V> valueTuple;
|
||||
|
||||
private RedisSerializer<?> hashKeySerializer;
|
||||
private final SerializationTuple<?> hashKeyTuple;
|
||||
|
||||
private RedisSerializer<?> hashValueSerializer;
|
||||
private final SerializationTuple<?> hashValueTuple;
|
||||
|
||||
private RedisSerializer<String> stringSerializer;
|
||||
private final SerializationTuple<String> stringTuple;
|
||||
|
||||
public ImmutableReactiveSerializationContext(ReactiveSerializationContextSupport<K, V> context) {
|
||||
|
||||
keySerializer = context.getKeySerializer();
|
||||
keyTuple = context.key();
|
||||
valueSerializer = context.getValueSerializer();
|
||||
valueTuple = context.value();
|
||||
hashKeySerializer = context.getHashKeySerializer();
|
||||
hashKeyTuple = context.hashKey();
|
||||
hashValueSerializer = context.getHashValueSerializer();
|
||||
hashValueTuple = context.hashValue();
|
||||
stringSerializer = context.getStringSerializer();
|
||||
stringTuple = context.string();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializationTuple<K> key() {
|
||||
return keyTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializationTuple<V> value() {
|
||||
return valueTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializationTuple<String> string() {
|
||||
return stringTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <HK> SerializationTuple<HK> hashKey() {
|
||||
return (SerializationTuple) hashKeyTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <HV> SerializationTuple<HV> hashValue() {
|
||||
return (SerializationTuple) hashValueTuple;
|
||||
}
|
||||
|
||||
public RedisSerializer<K> getKeySerializer() {
|
||||
return keySerializer;
|
||||
}
|
||||
|
||||
public RedisSerializer<V> getValueSerializer() {
|
||||
return valueSerializer;
|
||||
}
|
||||
|
||||
public RedisSerializer<?> getHashKeySerializer() {
|
||||
return hashKeySerializer;
|
||||
}
|
||||
|
||||
public RedisSerializer<?> getHashValueSerializer() {
|
||||
return hashValueSerializer;
|
||||
}
|
||||
|
||||
public RedisSerializer<String> getStringSerializer() {
|
||||
return stringSerializer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class MutableReactiveSerializationContext<K, V> extends ReactiveSerializationContextSupport<K, V> {
|
||||
|
||||
private RedisSerializer<K> keySerializer;
|
||||
private SerializationTuple<K> keyTuple = SerializationTuple.raw();
|
||||
|
||||
private RedisSerializer<V> valueSerializer;
|
||||
private SerializationTuple<V> valueTuple = SerializationTuple.raw();
|
||||
|
||||
private RedisSerializer<?> hashKeySerializer;
|
||||
private SerializationTuple<?> hashKeyTuple = SerializationTuple.raw();
|
||||
|
||||
private RedisSerializer<?> hashValueSerializer;
|
||||
private SerializationTuple<?> hashValueTuple = SerializationTuple.raw();
|
||||
|
||||
private RedisSerializer<String> stringSerializer = new StringRedisSerializer();
|
||||
private SerializationTuple<String> stringTuple = SerializationTuple.fromSerializer(stringSerializer);
|
||||
|
||||
@Override
|
||||
public SerializationTuple<K> key() {
|
||||
return keyTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializationTuple<V> value() {
|
||||
return valueTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializationTuple<String> string() {
|
||||
return stringTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <HK> SerializationTuple<HK> hashKey() {
|
||||
return (SerializationTuple) hashKeyTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <HV> SerializationTuple<HV> hashValue() {
|
||||
return (SerializationTuple) hashValueTuple;
|
||||
}
|
||||
|
||||
public RedisSerializer<K> getKeySerializer() {
|
||||
return keySerializer;
|
||||
}
|
||||
|
||||
public void setKeySerializer(RedisSerializer<K> keySerializer) {
|
||||
this.keySerializer = keySerializer;
|
||||
this.keyTuple = SerializationTuple.fromSerializer(keySerializer);
|
||||
}
|
||||
|
||||
public RedisSerializer<V> getValueSerializer() {
|
||||
return valueSerializer;
|
||||
}
|
||||
|
||||
public void setValueSerializer(RedisSerializer<V> valueSerializer) {
|
||||
this.valueSerializer = valueSerializer;
|
||||
this.valueTuple = SerializationTuple.fromSerializer(valueSerializer);
|
||||
}
|
||||
|
||||
public RedisSerializer<?> getHashKeySerializer() {
|
||||
return hashKeySerializer;
|
||||
}
|
||||
|
||||
public void setHashKeySerializer(RedisSerializer<?> hashKeySerializer) {
|
||||
this.hashKeySerializer = hashKeySerializer;
|
||||
this.hashKeyTuple = SerializationTuple.fromSerializer(hashKeySerializer);
|
||||
}
|
||||
|
||||
public RedisSerializer<?> getHashValueSerializer() {
|
||||
return hashValueSerializer;
|
||||
}
|
||||
|
||||
public void setHashValueSerializer(RedisSerializer<?> hashValueSerializer) {
|
||||
this.hashValueSerializer = hashValueSerializer;
|
||||
this.hashValueTuple = SerializationTuple.fromSerializer(hashValueSerializer);
|
||||
}
|
||||
|
||||
public RedisSerializer<String> getStringSerializer() {
|
||||
return stringSerializer;
|
||||
}
|
||||
|
||||
public void setStringSerializer(RedisSerializer<String> stringSerializer) {
|
||||
this.stringSerializer = stringSerializer;
|
||||
this.stringTuple = SerializationTuple.fromSerializer(stringSerializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Redis set specific operations.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveSetOperations<K, V> {
|
||||
|
||||
/**
|
||||
* Add given {@code values} to set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sadd">Redis Documentation: SADD</a>
|
||||
*/
|
||||
Mono<Long> add(K key, V... values);
|
||||
|
||||
/**
|
||||
* Remove given {@code values} from set at {@code key} and return the number of removed elements.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/srem">Redis Documentation: SREM</a>
|
||||
*/
|
||||
Mono<Long> remove(K key, Object... values);
|
||||
|
||||
/**
|
||||
* Remove and return a random member from set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/spop">Redis Documentation: SPOP</a>
|
||||
*/
|
||||
Mono<V> pop(K key);
|
||||
|
||||
/**
|
||||
* Move {@code value} from {@code key} to {@code destKey}
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param value
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/smove">Redis Documentation: SMOVE</a>
|
||||
*/
|
||||
Mono<Boolean> move(K sourceKey, V value, K destKey);
|
||||
|
||||
/**
|
||||
* Get size of set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/scard">Redis Documentation: SCARD</a>
|
||||
*/
|
||||
Mono<Long> size(K key);
|
||||
|
||||
/**
|
||||
* Check if set at {@code key} contains {@code value}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param o
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sismember">Redis Documentation: SISMEMBER</a>
|
||||
*/
|
||||
Mono<Boolean> isMember(K key, Object o);
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at {@code key} and {@code otherKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sinter">Redis Documentation: SINTER</a>
|
||||
*/
|
||||
Mono<Set<V>> intersect(K key, K otherKey);
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at {@code key} and {@code otherKeys}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKeys must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sinter">Redis Documentation: SINTER</a>
|
||||
*/
|
||||
Mono<Set<V>> intersect(K key, Collection<K> otherKeys);
|
||||
|
||||
/**
|
||||
* Intersect all given sets at {@code key} and {@code otherKey} and store result in {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKey must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sinterstore">Redis Documentation: SINTERSTORE</a>
|
||||
*/
|
||||
Mono<Long> intersectAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
/**
|
||||
* Intersect all given sets at {@code key} and {@code otherKeys} and store result in {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKeys must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sinterstore">Redis Documentation: SINTERSTORE</a>
|
||||
*/
|
||||
Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
/**
|
||||
* Union all sets at given {@code keys} and {@code otherKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sunion">Redis Documentation: SUNION</a>
|
||||
*/
|
||||
Mono<Set<V>> union(K key, K otherKey);
|
||||
|
||||
/**
|
||||
* Union all sets at given {@code keys} and {@code otherKeys}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKeys must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sunion">Redis Documentation: SUNION</a>
|
||||
*/
|
||||
Mono<Set<V>> union(K key, Collection<K> otherKeys);
|
||||
|
||||
/**
|
||||
* Union all sets at given {@code key} and {@code otherKey} and store result in {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKey must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sunionstore">Redis Documentation: SUNIONSTORE</a>
|
||||
*/
|
||||
Mono<Long> unionAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
/**
|
||||
* Union all sets at given {@code key} and {@code otherKeys} and store result in {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKeys must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sunionstore">Redis Documentation: SUNIONSTORE</a>
|
||||
*/
|
||||
Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
/**
|
||||
* Diff all sets for given {@code key} and {@code otherKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sdiff">Redis Documentation: SDIFF</a>
|
||||
*/
|
||||
Mono<Set<V>> difference(K key, K otherKey);
|
||||
|
||||
/**
|
||||
* Diff all sets for given {@code key} and {@code otherKeys}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKeys must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sdiff">Redis Documentation: SDIFF</a>
|
||||
*/
|
||||
Mono<Set<V>> difference(K key, Collection<K> otherKeys);
|
||||
|
||||
/**
|
||||
* Diff all sets for given {@code key} and {@code otherKey} and store result in {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKey must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sdiffstore">Redis Documentation: SDIFFSTORE</a>
|
||||
*/
|
||||
Mono<Long> differenceAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
/**
|
||||
* Diff all sets for given {@code key} and {@code otherKeys} and store result in {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKeys must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/sdiffstore">Redis Documentation: SDIFFSTORE</a>
|
||||
*/
|
||||
Mono<Long> differenceAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
/**
|
||||
* Get all elements of set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/smembers">Redis Documentation: SMEMBERS</a>
|
||||
*/
|
||||
Mono<Set<V>> members(K key);
|
||||
|
||||
/**
|
||||
* Get random element from set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/srandmember">Redis Documentation: SRANDMEMBER</a>
|
||||
*/
|
||||
Mono<V> randomMember(K key);
|
||||
|
||||
/**
|
||||
* Get {@code count} distinct random elements from set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param count
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/srandmember">Redis Documentation: SRANDMEMBER</a>
|
||||
*/
|
||||
Mono<Set<V>> distinctRandomMembers(K key, long count);
|
||||
|
||||
/**
|
||||
* Get {@code count} random elements from set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param count
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/srandmember">Redis Documentation: SRANDMEMBER</a>
|
||||
*/
|
||||
Mono<List<V>> randomMembers(K key, long count);
|
||||
|
||||
/**
|
||||
* Removes the given {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
*/
|
||||
Mono<Boolean> delete(K key);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Reactive Redis operations for simple (or in Redis terminology 'string') values.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveValueOperations<K, V> {
|
||||
|
||||
/**
|
||||
* Set {@code value} for {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @see <a href="http://redis.io/commands/set">Redis Documentation: SET</a>
|
||||
*/
|
||||
Mono<Boolean> set(K key, V value);
|
||||
|
||||
/**
|
||||
* Set the {@code value} and expiration {@code timeout} for {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @param timeout must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/setex">Redis Documentation: SETEX</a>
|
||||
*/
|
||||
Mono<Boolean> set(K key, V value, Duration timeout);
|
||||
|
||||
/**
|
||||
* Set {@code key} to hold the string {@code value} if {@code key} is absent.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @see <a href="http://redis.io/commands/setnx">Redis Documentation: SETNX</a>
|
||||
*/
|
||||
Mono<Boolean> setIfAbsent(K key, V value);
|
||||
|
||||
/**
|
||||
* Set {@code key} to hold the string {@code value} if {@code key} is present.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @see <a href="http://redis.io/commands/set">Redis Documentation: SET</a>
|
||||
*/
|
||||
Mono<Boolean> setIfPresent(K key, V value);
|
||||
|
||||
/**
|
||||
* Set multiple keys to multiple values using key-value pairs provided in {@code tuple}.
|
||||
*
|
||||
* @param map must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/mset">Redis Documentation: MSET</a>
|
||||
*/
|
||||
Mono<Boolean> multiSet(Map<? extends K, ? extends V> map);
|
||||
|
||||
/**
|
||||
* Set multiple keys to multiple values using key-value pairs provided in {@code tuple} only if the provided key does
|
||||
* not exist.
|
||||
*
|
||||
* @param map must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/mset">Redis Documentation: MSET</a>
|
||||
*/
|
||||
Mono<Boolean> multiSetIfAbsent(Map<? extends K, ? extends V> map);
|
||||
|
||||
/**
|
||||
* Get the value of {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/get">Redis Documentation: GET</a>
|
||||
*/
|
||||
Mono<V> get(Object key);
|
||||
|
||||
/**
|
||||
* Set {@code value} of {@code key} and return its old value.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/getset">Redis Documentation: GETSET</a>
|
||||
*/
|
||||
Mono<V> getAndSet(K key, V value);
|
||||
|
||||
/**
|
||||
* Get multiple {@code keys}. Values are returned in the order of the requested keys.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/mget">Redis Documentation: MGET</a>
|
||||
*/
|
||||
Mono<List<V>> multiGet(Collection<K> keys);
|
||||
|
||||
/**
|
||||
* Append a {@code value} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @see <a href="http://redis.io/commands/append">Redis Documentation: APPEND</a>
|
||||
*/
|
||||
Mono<Long> append(K key, String value);
|
||||
|
||||
/**
|
||||
* Get a substring of value of {@code key} between {@code begin} and {@code end}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param start
|
||||
* @param end
|
||||
* @see <a href="http://redis.io/commands/getrange">Redis Documentation: GETRANGE</a>
|
||||
*/
|
||||
Mono<String> get(K key, long start, long end);
|
||||
|
||||
/**
|
||||
* Overwrite parts of {@code key} starting at the specified {@code offset} with given {@code value}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value
|
||||
* @param offset
|
||||
* @see <a href="http://redis.io/commands/setrange">Redis Documentation: SETRANGE</a>
|
||||
*/
|
||||
Mono<Long> set(K key, V value, long offset);
|
||||
|
||||
/**
|
||||
* Get the length of the value stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/strlen">Redis Documentation: STRLEN</a>
|
||||
*/
|
||||
Mono<Long> size(K key);
|
||||
|
||||
/**
|
||||
* Sets the bit at {@code offset} in value stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param offset
|
||||
* @param value
|
||||
* @see <a href="http://redis.io/commands/setbit">Redis Documentation: SETBIT</a>
|
||||
*/
|
||||
Mono<Boolean> setBit(K key, long offset, boolean value);
|
||||
|
||||
/**
|
||||
* « Get the bit value at {@code offset} of value at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param offset
|
||||
* @see <a href="http://redis.io/commands/setbit">Redis Documentation: GETBIT</a>
|
||||
*/
|
||||
Mono<Boolean> getBit(K key, long offset);
|
||||
|
||||
/**
|
||||
* Removes the given {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
*/
|
||||
Mono<Boolean> delete(K key);
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.core;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Limit;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
|
||||
|
||||
/**
|
||||
* Redis ZSet/sorted set specific operations.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveZSetOperations<K, V> {
|
||||
|
||||
/**
|
||||
* Add {@code value} to a sorted set at {@code key}, or update its {@code score} if it already exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param score the score.
|
||||
* @param value the value.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zadd">Redis Documentation: ZADD</a>
|
||||
*/
|
||||
Mono<Boolean> add(K key, V value, double score);
|
||||
|
||||
/**
|
||||
* Add {@code tuples} to a sorted set at {@code key}, or update their score if it already exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param tuples the score.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zadd">Redis Documentation: ZADD</a>
|
||||
*/
|
||||
Mono<Long> addAll(K key, Collection<? extends TypedTuple<V>> tuples);
|
||||
|
||||
/**
|
||||
* Add {@code tuples} to a sorted set at {@code key}, or update its {@code score} if it already exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param tuples must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zadd">Redis Documentation: ZADD</a>
|
||||
*/
|
||||
// TODO
|
||||
// Mono<Long> add(K key, Set<TypedTuple<V>> tuples);
|
||||
|
||||
/**
|
||||
* Remove {@code values} from sorted set. Return number of removed elements.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrem">Redis Documentation: ZREM</a>
|
||||
*/
|
||||
Mono<Long> remove(K key, Object... values);
|
||||
|
||||
/**
|
||||
* Increment the score of element with {@code value} in sorted set by {@code increment}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param delta
|
||||
* @param value the value.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zincrby">Redis Documentation: ZINCRBY</a>
|
||||
*/
|
||||
Mono<Double> incrementScore(K key, V value, double delta);
|
||||
|
||||
/**
|
||||
* Determine the index of element with {@code value} in a sorted set.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param o the value.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrank">Redis Documentation: ZRANK</a>
|
||||
*/
|
||||
Mono<Long> rank(K key, Object o);
|
||||
|
||||
/**
|
||||
* Determine the index of element with {@code value} in a sorted set when scored high to low.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param o the value.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrevrank">Redis Documentation: ZREVRANK</a>
|
||||
*/
|
||||
Mono<Long> reverseRank(K key, Object o);
|
||||
|
||||
/**
|
||||
* Get elements between {@code start} and {@code end} from sorted set.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrange">Redis Documentation: ZRANGE</a>
|
||||
*/
|
||||
Mono<Set<V>> range(K key, Range<Long> range);
|
||||
|
||||
/**
|
||||
* Get set of {@link Tuple}s between {@code start} and {@code end} from sorted set.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrange">Redis Documentation: ZRANGE</a>
|
||||
*/
|
||||
Mono<Set<TypedTuple<V>>> rangeWithScores(K key, Range<Long> range);
|
||||
|
||||
/**
|
||||
* Get elements where score is between {@code min} and {@code max} from sorted set.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrangebyscore">Redis Documentation: ZRANGEBYSCORE</a>
|
||||
*/
|
||||
Mono<Set<V>> rangeByScore(K key, Range<Double> range);
|
||||
|
||||
/**
|
||||
* Get set of {@link Tuple}s where score is between {@code min} and {@code max} from sorted set.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrangebyscore">Redis Documentation: ZRANGEBYSCORE</a>
|
||||
*/
|
||||
Mono<Set<TypedTuple<V>>> rangeByScoreWithScores(K key, Range<Double> range);
|
||||
|
||||
/**
|
||||
* Get elements in range from {@code start} to {@code end} where score is between {@code min} and {@code max} from
|
||||
* sorted set.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @param limit
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrangebyscore">Redis Documentation: ZRANGEBYSCORE</a>
|
||||
*/
|
||||
Mono<Set<V>> rangeByScore(K key, Range<Double> range, Limit limit);
|
||||
|
||||
/**
|
||||
* Get set of {@link Tuple}s in range from {@code start} to {@code end} where score is between {@code min} and
|
||||
* {@code max} from sorted set.
|
||||
*
|
||||
* @param key
|
||||
* @param range
|
||||
* @param limit
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrangebyscore">Redis Documentation: ZRANGEBYSCORE</a>
|
||||
*/
|
||||
Mono<Set<TypedTuple<V>>> rangeByScoreWithScores(K key, Range<Double> range, Limit limit);
|
||||
|
||||
/**
|
||||
* Get elements in range from {@code start} to {@code end} from sorted set ordered from high to low.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrevrange">Redis Documentation: ZREVRANGE</a>
|
||||
*/
|
||||
Mono<Set<V>> reverseRange(K key, Range<Long> range);
|
||||
|
||||
/**
|
||||
* Get set of {@link Tuple}s in range from {@code start} to {@code end} from sorted set ordered from high to low.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrevrange">Redis Documentation: ZREVRANGE</a>
|
||||
*/
|
||||
Mono<Set<TypedTuple<V>>> reverseRangeWithScores(K key, Range<Long> range);
|
||||
|
||||
/**
|
||||
* Get elements where score is between {@code min} and {@code max} from sorted set ordered from high to low.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrevrange">Redis Documentation: ZREVRANGE</a>
|
||||
*/
|
||||
Mono<Set<V>> reverseRangeByScore(K key, Range<Double> range);
|
||||
|
||||
/**
|
||||
* Get set of {@link Tuple} where score is between {@code min} and {@code max} from sorted set ordered from high to
|
||||
* low.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrevrangebyscore">Redis Documentation: ZREVRANGEBYSCORE</a>
|
||||
*/
|
||||
Mono<Set<TypedTuple<V>>> reverseRangeByScoreWithScores(K key, Range<Double> range);
|
||||
|
||||
/**
|
||||
* Get elements in range from {@code start} to {@code end} where score is between {@code min} and {@code max} from
|
||||
* sorted set ordered high -> low.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @param limit
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrevrangebyscore">Redis Documentation: ZREVRANGEBYSCORE</a>
|
||||
*/
|
||||
Mono<Set<V>> reverseRangeByScore(K key, Range<Double> range, Limit limit);
|
||||
|
||||
/**
|
||||
* Get set of {@link Tuple} in range from {@code start} to {@code end} where score is between {@code min} and
|
||||
* {@code max} from sorted set ordered high -> low.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @param limit
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrevrangebyscore">Redis Documentation: ZREVRANGEBYSCORE</a>
|
||||
*/
|
||||
Mono<Set<TypedTuple<V>>> reverseRangeByScoreWithScores(K key, Range<Double> range, Limit limit);
|
||||
|
||||
/**
|
||||
* Count number of elements within sorted set with scores between {@code min} and {@code max}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zcount">Redis Documentation: ZCOUNT</a>
|
||||
*/
|
||||
Mono<Long> count(K key, Range<Double> range);
|
||||
|
||||
/**
|
||||
* Returns the number of elements of the sorted set stored with given {@code key}.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zcard">Redis Documentation: ZCARD</a>
|
||||
*/
|
||||
Mono<Long> size(K key);
|
||||
|
||||
/**
|
||||
* Get the score of element with {@code value} from sorted set with key {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param o the value.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrem">Redis Documentation: ZREM</a>
|
||||
*/
|
||||
Mono<Double> score(K key, Object o);
|
||||
|
||||
/**
|
||||
* Remove elements in range between {@code start} and {@code end} from sorted set with {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zremrangebyrank">Redis Documentation: ZREMRANGEBYRANK</a>
|
||||
*/
|
||||
Mono<Long> removeRange(K key, Range<Long> range);
|
||||
|
||||
/**
|
||||
* Remove elements with scores between {@code min} and {@code max} from sorted set with {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zremrangebyscore">Redis Documentation: ZREMRANGEBYSCORE</a>
|
||||
*/
|
||||
Mono<Long> removeRangeByScore(K key, Range<Double> range);
|
||||
|
||||
/**
|
||||
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKey must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
|
||||
*/
|
||||
Mono<Long> unionAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
/**
|
||||
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKeys must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
|
||||
*/
|
||||
Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
/**
|
||||
* Intersect sorted sets at {@code key} and {@code otherKey} and store result in destination {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKey must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zinterstore">Redis Documentation: ZINTERSTORE</a>
|
||||
*/
|
||||
Mono<Long> intersectAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
/**
|
||||
* Intersect sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param otherKeys must not be {@literal null}.
|
||||
* @param destKey must not be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zinterstore">Redis Documentation: ZINTERSTORE</a>
|
||||
*/
|
||||
Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
/**
|
||||
* Get all elements with lexicographical ordering from {@literal ZSET} at {@code key} with a value between
|
||||
* {@link Range#getLowerBound()} and {@link Range#getUpperBound()}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/zrangebylex">Redis Documentation: ZRANGEBYLEX</a>
|
||||
*/
|
||||
Mono<Set<V>> rangeByLex(K key, Range<String> range);
|
||||
|
||||
/**
|
||||
* Get all elements {@literal n} elements, where {@literal n = } {@link Limit#getCount()}, starting at
|
||||
* {@link Limit#getOffset()} with lexicographical ordering from {@literal ZSET} at {@code key} with a value between
|
||||
* {@link Range#getLowerBound()} and {@link Range#getUpperBound()}.
|
||||
*
|
||||
* @param key must not be {@literal null}
|
||||
* @param range must not be {@literal null}.
|
||||
* @param limit can be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrangebylex">Redis Documentation: ZRANGEBYLEX</a>
|
||||
*/
|
||||
Mono<Set<V>> rangeByLex(K key, Range<String> range, Limit limit);
|
||||
|
||||
/**
|
||||
* Get all elements with reverse lexicographical ordering from {@literal ZSET} at {@code key} with a value between
|
||||
* {@link Range#getLowerBound()} and {@link Range#getUpperBound()}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param range must not be {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/zrevrangebylex">Redis Documentation: ZREVRANGEBYLEX</a>
|
||||
*/
|
||||
Mono<Set<V>> reverseRangeByLex(K key, Range<String> range);
|
||||
|
||||
/**
|
||||
* Get all elements {@literal n} elements, where {@literal n = } {@link Limit#getCount()}, starting at
|
||||
* {@link Limit#getOffset()} with reverse lexicographical ordering from {@literal ZSET} at {@code key} with a value
|
||||
* between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}.
|
||||
*
|
||||
* @param key must not be {@literal null}
|
||||
* @param range must not be {@literal null}.
|
||||
* @param limit can be {@literal null}.
|
||||
* @return
|
||||
* @see <a href="http://redis.io/commands/zrevrangebylex">Redis Documentation: ZREVRANGEBYLEX</a>
|
||||
*/
|
||||
Mono<Set<V>> reverseRangeByLex(K key, Range<String> range, Limit limit);
|
||||
|
||||
/**
|
||||
* Removes the given {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
*/
|
||||
Mono<Boolean> delete(K key);
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
import org.springframework.data.redis.core.TimeToLive;
|
||||
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -28,7 +29,7 @@ import org.springframework.util.ClassUtils;
|
||||
* {@link KeyspaceConfiguration} allows programmatic setup of keyspaces and time to live options for certain types. This
|
||||
* is suitable for cases where there is no option to use the equivalent {@link RedisHash} or {@link TimeToLive}
|
||||
* annotations.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
@@ -44,9 +45,36 @@ public class KeyspaceConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
@EnableRedisRepositories(keyspaceConfiguration = MyKeyspaceConfiguration.class)
|
||||
static class MyConfig {
|
||||
|
||||
}
|
||||
|
||||
static class MyKeyspaceConfiguration extends KeyspaceConfiguration {
|
||||
|
||||
@Override
|
||||
protected Iterable<KeyspaceSettings> initialConfiguration() {
|
||||
return super.initialConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSettingsFor(Class<?> type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyspaceSettings getKeyspaceSettings(Class<?> type) {
|
||||
|
||||
KeyspaceSettings keyspaceSettings = new KeyspaceSettings(type, "my-keyspace");
|
||||
keyspaceSettings.setTimeToLive(3600L);
|
||||
|
||||
return keyspaceSettings;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific {@link KeyspaceSettings} are available for given type.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return true if settings exist.
|
||||
*/
|
||||
@@ -78,7 +106,7 @@ public class KeyspaceConfiguration {
|
||||
|
||||
/**
|
||||
* Get the {@link KeyspaceSettings} for given type.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}
|
||||
* @return {@literal null} if no settings configured.
|
||||
*/
|
||||
@@ -98,7 +126,7 @@ public class KeyspaceConfiguration {
|
||||
|
||||
/**
|
||||
* Customization hook.
|
||||
*
|
||||
*
|
||||
* @return must not return {@literal null}.
|
||||
*/
|
||||
protected Iterable<KeyspaceSettings> initialConfiguration() {
|
||||
@@ -107,7 +135,7 @@ public class KeyspaceConfiguration {
|
||||
|
||||
/**
|
||||
* Add {@link KeyspaceSettings} for type.
|
||||
*
|
||||
*
|
||||
* @param keyspaceSettings must not be {@literal null}.
|
||||
*/
|
||||
public void addKeyspaceSettings(KeyspaceSettings keyspaceSettings) {
|
||||
@@ -171,7 +199,7 @@ public class KeyspaceConfiguration {
|
||||
|
||||
/**
|
||||
* Marker class indicating no settings defined.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 the original author 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,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core.types;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
@@ -22,7 +23,7 @@ import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Expiration holds a value with its associated {@link TimeUnit}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 1.7
|
||||
@@ -34,7 +35,7 @@ public class Expiration {
|
||||
|
||||
/**
|
||||
* Creates new {@link Expiration}.
|
||||
*
|
||||
*
|
||||
* @param expirationTime can be {@literal null}. Defaulted to {@link TimeUnit#SECONDS}
|
||||
* @param timeUnit
|
||||
*/
|
||||
@@ -46,7 +47,7 @@ public class Expiration {
|
||||
|
||||
/**
|
||||
* Get the expiration time converted into {@link TimeUnit#MILLISECONDS}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getExpirationTimeInMilliseconds() {
|
||||
@@ -55,7 +56,7 @@ public class Expiration {
|
||||
|
||||
/**
|
||||
* Get the expiration time converted into {@link TimeUnit#SECONDS}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getExpirationTimeInSeconds() {
|
||||
@@ -64,7 +65,7 @@ public class Expiration {
|
||||
|
||||
/**
|
||||
* Get the expiration time.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getExpirationTime() {
|
||||
@@ -73,7 +74,7 @@ public class Expiration {
|
||||
|
||||
/**
|
||||
* Get the time unit for the expiration time.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public TimeUnit getTimeUnit() {
|
||||
@@ -82,7 +83,7 @@ public class Expiration {
|
||||
|
||||
/**
|
||||
* Get the expiration time converted into the desired {@code targetTimeUnit}.
|
||||
*
|
||||
*
|
||||
* @param targetTimeUnit must not {@literal null}.
|
||||
* @return
|
||||
* @throws IllegalArgumentException
|
||||
@@ -95,7 +96,7 @@ public class Expiration {
|
||||
|
||||
/**
|
||||
* Creates new {@link Expiration} with {@link TimeUnit#SECONDS}.
|
||||
*
|
||||
*
|
||||
* @param expirationTime
|
||||
* @return
|
||||
*/
|
||||
@@ -105,7 +106,7 @@ public class Expiration {
|
||||
|
||||
/**
|
||||
* Creates new {@link Expiration} with {@link TimeUnit#MILLISECONDS}.
|
||||
*
|
||||
*
|
||||
* @param expirationTime
|
||||
* @return
|
||||
*/
|
||||
@@ -116,8 +117,8 @@ public class Expiration {
|
||||
/**
|
||||
* Creates new {@link Expiration} with the provided {@link TimeUnit}. Greater units than {@link TimeUnit#SECONDS} are
|
||||
* converted to {@link TimeUnit#SECONDS}. Units smaller than {@link TimeUnit#MILLISECONDS} are converted to
|
||||
* {@link TimeUnit#MILLISECONDS} and can lose precision since {@link TimeUnit#MILLISECONDS} is the smallest granularity
|
||||
* supported by Redis.
|
||||
* {@link TimeUnit#MILLISECONDS} and can lose precision since {@link TimeUnit#MILLISECONDS} is the smallest
|
||||
* granularity supported by Redis.
|
||||
*
|
||||
* @param expirationTime
|
||||
* @param timeUnit can be {@literal null}. Defaulted to {@link TimeUnit#SECONDS}
|
||||
@@ -138,9 +139,29 @@ public class Expiration {
|
||||
return new Expiration(expirationTime, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Expiration} with the provided {@link java.time.Duration}. Durations with at least
|
||||
* {@link TimeUnit#SECONDS} resolution use seconds, durations using milliseconds use {@link TimeUnit#MILLISECONDS}
|
||||
* resolution.
|
||||
*
|
||||
* @param duration must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.0
|
||||
*/
|
||||
public static Expiration from(Duration duration) {
|
||||
|
||||
Assert.notNull(duration, "Duration must not be null!");
|
||||
|
||||
if (duration.toMillis() % 1000 == 0) {
|
||||
return new Expiration(duration.getSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
return new Expiration(duration.toMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new persistent {@link Expiration}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Expiration persistent() {
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.serializer;
|
||||
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext.ReactiveSerializationContextBuilder;
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext.SerializationTuple;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ReactiveSerializationContextBuilder}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultReactiveSerializationContextBuilder<K, V> implements ReactiveSerializationContextBuilder<K, V> {
|
||||
|
||||
private SerializationTuple<K> keyTuple;
|
||||
|
||||
private SerializationTuple<V> valueTuple;
|
||||
|
||||
private SerializationTuple<?> hashKeyTuple;
|
||||
|
||||
private SerializationTuple<?> hashValueTuple;
|
||||
|
||||
private SerializationTuple<String> stringTuple = SerializationTuple.fromSerializer(new StringRedisSerializer());
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> key(SerializationTuple<K> tuple) {
|
||||
|
||||
Assert.notNull(tuple, "SerializationTuple must not be null!");
|
||||
|
||||
this.keyTuple = tuple;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> key(RedisElementReader<K> reader, RedisElementWriter<K> writer) {
|
||||
return key(SerializationTuple.just(reader, writer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> key(RedisSerializer<K> serializer) {
|
||||
return key(SerializationTuple.fromSerializer(serializer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> value(SerializationTuple<V> tuple) {
|
||||
|
||||
Assert.notNull(tuple, "SerializationTuple must not be null!");
|
||||
|
||||
this.valueTuple = tuple;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> value(RedisElementReader<V> reader, RedisElementWriter<V> writer) {
|
||||
return value(SerializationTuple.just(reader, writer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> value(RedisSerializer<V> serializer) {
|
||||
return value(SerializationTuple.fromSerializer(serializer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> hashKey(SerializationTuple<?> tuple) {
|
||||
|
||||
Assert.notNull(tuple, "SerializationTuple must not be null!");
|
||||
|
||||
this.hashKeyTuple = tuple;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> hashKey(RedisElementReader<?> reader, RedisElementWriter<?> writer) {
|
||||
return hashKey(SerializationTuple.just(reader, writer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> hashKey(RedisSerializer<?> serializer) {
|
||||
return hashKey(SerializationTuple.fromSerializer(serializer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> hashValue(SerializationTuple<?> tuple) {
|
||||
|
||||
Assert.notNull(tuple, "SerializationTuple must not be null!");
|
||||
|
||||
this.hashValueTuple = tuple;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> hashValue(RedisElementReader<?> reader,
|
||||
RedisElementWriter<?> writer) {
|
||||
return hashValue(SerializationTuple.just(reader, writer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> hashValue(RedisSerializer<?> serializer) {
|
||||
return hashValue(SerializationTuple.fromSerializer(serializer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> string(SerializationTuple<String> tuple) {
|
||||
|
||||
Assert.notNull(tuple, "SerializationTuple must not be null!");
|
||||
|
||||
this.hashValueTuple = tuple;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> string(RedisElementReader<String> reader,
|
||||
RedisElementWriter<String> writer) {
|
||||
return string(SerializationTuple.just(reader, writer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContextBuilder<K, V> string(RedisSerializer<String> serializer) {
|
||||
return string(SerializationTuple.fromSerializer(serializer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSerializationContext<K, V> build() {
|
||||
|
||||
Assert.notNull(keyTuple, "Key SerializationTuple must not be null!");
|
||||
Assert.notNull(valueTuple, "Value SerializationTuple must not be null!");
|
||||
Assert.notNull(hashKeyTuple, "HashKey SerializationTuple must not be null!");
|
||||
Assert.notNull(hashValueTuple, "ValueKey SerializationTuple must not be null!");
|
||||
|
||||
return new DefaultReactiveSerializationContext<K, V>(keyTuple, valueTuple, hashKeyTuple, hashValueTuple,
|
||||
stringTuple);
|
||||
}
|
||||
|
||||
static class DefaultReactiveSerializationContext<K, V> implements ReactiveSerializationContext<K, V> {
|
||||
|
||||
private final SerializationTuple<K> keyTuple;
|
||||
|
||||
private final SerializationTuple<V> valueTuple;
|
||||
|
||||
private final SerializationTuple<?> hashKeyTuple;
|
||||
|
||||
private final SerializationTuple<?> hashValueTuple;
|
||||
|
||||
private final SerializationTuple<String> stringTuple;
|
||||
|
||||
public DefaultReactiveSerializationContext(SerializationTuple<K> keyTuple, SerializationTuple<V> valueTuple,
|
||||
SerializationTuple<?> hashKeyTuple, SerializationTuple<?> hashValueTuple,
|
||||
SerializationTuple<String> stringTuple) {
|
||||
|
||||
this.keyTuple = keyTuple;
|
||||
this.valueTuple = valueTuple;
|
||||
this.hashKeyTuple = hashKeyTuple;
|
||||
this.hashValueTuple = hashValueTuple;
|
||||
this.stringTuple = stringTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializationTuple<K> key() {
|
||||
return keyTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializationTuple<V> value() {
|
||||
return valueTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <HK> SerializationTuple<HK> hashKey() {
|
||||
return (SerializationTuple) hashKeyTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <HV> SerializationTuple<HV> hashValue() {
|
||||
return (SerializationTuple) hashValueTuple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializationTuple<String> string() {
|
||||
return stringTuple;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.serializer;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link RedisElementReader}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class DefaultRedisElementReader<T> implements RedisElementReader<T> {
|
||||
|
||||
private final RedisSerializer<T> serializer;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.serializer.RedisElementReader#read(java.nio.ByteBuffer)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T read(ByteBuffer buffer) {
|
||||
|
||||
if (serializer == null) {
|
||||
return (T) buffer;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
buffer.get(bytes);
|
||||
|
||||
return serializer.deserialize(bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.serializer;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link RedisElementWriter}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class DefaultRedisElementWriter<T> implements RedisElementWriter<T> {
|
||||
|
||||
private final RedisSerializer<T> serializer;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.serializer.RedisElementWriter#write(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public ByteBuffer write(T value) {
|
||||
|
||||
if (serializer == null) {
|
||||
|
||||
if (value instanceof byte[]) {
|
||||
return ByteBuffer.wrap((byte[]) value);
|
||||
}
|
||||
|
||||
if (value instanceof ByteBuffer) {
|
||||
return (ByteBuffer) value;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot serialize value without a serializer");
|
||||
}
|
||||
|
||||
return ByteBuffer.wrap(serializer.serialize((T) value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.serializer;
|
||||
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext.SerializationTuple;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link SerializationTuple}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
class DefaultSerializationTuple<T> implements SerializationTuple<T> {
|
||||
|
||||
private final RedisElementReader<T> reader;
|
||||
|
||||
private final RedisElementWriter<T> writer;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected DefaultSerializationTuple(RedisElementReader<? extends T> reader, RedisElementWriter<? extends T> writer) {
|
||||
|
||||
this.reader = (RedisElementReader) reader;
|
||||
this.writer = (RedisElementWriter) writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisElementReader<T> getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisElementWriter<T> getWriter() {
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.serializer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Serialization context for reactive use.
|
||||
* <p>
|
||||
* This context provides {@link SerializationTuple}s for key, value, hash-key (field), hash-value and {@link String}
|
||||
* serialization and deserialization.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
* @see RedisElementWriter
|
||||
* @see RedisElementReader
|
||||
*/
|
||||
public interface ReactiveSerializationContext<K, V> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReactiveSerializationContextBuilder}.
|
||||
*
|
||||
* @param <K> expected key type.
|
||||
* @param <V> expected value type.
|
||||
* @return a new {@link ReactiveSerializationContextBuilder}.
|
||||
*/
|
||||
static <K, V> ReactiveSerializationContextBuilder<K, V> builder() {
|
||||
return new DefaultReactiveSerializationContextBuilder<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link SerializationTuple} for key-typed serialization and deserialization.
|
||||
*/
|
||||
SerializationTuple<K> key();
|
||||
|
||||
/**
|
||||
* @return {@link SerializationTuple} for value-typed serialization and deserialization.
|
||||
*/
|
||||
SerializationTuple<V> value();
|
||||
|
||||
/**
|
||||
* @return {@link SerializationTuple} for hash-key-typed serialization and deserialization.
|
||||
*/
|
||||
<HK> SerializationTuple<HK> hashKey();
|
||||
|
||||
/**
|
||||
* @return {@link SerializationTuple} for hash-value-typed serialization and deserialization.
|
||||
*/
|
||||
<HV> SerializationTuple<HV> hashValue();
|
||||
|
||||
/**
|
||||
* @return {@link SerializationTuple} for {@link String}-typed serialization and deserialization.
|
||||
*/
|
||||
SerializationTuple<String> string();
|
||||
|
||||
/**
|
||||
* Typed serialization tuple.
|
||||
*/
|
||||
interface SerializationTuple<T> {
|
||||
|
||||
/**
|
||||
* Creates a {@link SerializationTuple} adapter given {@link RedisSerializer}.
|
||||
*
|
||||
* @param serializer must not be {@literal null}.
|
||||
* @return a {@link SerializationTuple} adapter for {@link RedisSerializer}.
|
||||
*/
|
||||
static <T> SerializationTuple<T> fromSerializer(RedisSerializer<T> serializer) {
|
||||
|
||||
Assert.notNull(serializer, "RedisSerializer must not be null!");
|
||||
|
||||
return new RedisSerializerTupleAdapter<T>(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link SerializationTuple} adapter given {@link RedisElementReader} and {@link RedisElementWriter}.
|
||||
*
|
||||
* @param reader must not be {@literal null}.
|
||||
* @param writer must not be {@literal null}.
|
||||
* @return a {@link SerializationTuple} encapsulating {@link RedisElementReader} and {@link RedisElementWriter}.
|
||||
*/
|
||||
static <T> SerializationTuple<T> just(RedisElementReader<? extends T> reader,
|
||||
RedisElementWriter<? extends T> writer) {
|
||||
|
||||
Assert.notNull(reader, "RedisElementReader must not be null!");
|
||||
Assert.notNull(writer, "RedisElementWriter must not be null!");
|
||||
|
||||
return new DefaultSerializationTuple<>(reader, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a pass-thru {@link SerializationTuple} to pass-thru {@link ByteBuffer} objects.
|
||||
*
|
||||
* @return a pass-thru {@link SerializationTuple}.
|
||||
*/
|
||||
static <T> SerializationTuple<T> raw() {
|
||||
return RedisSerializerTupleAdapter.raw();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link RedisElementReader}.
|
||||
*/
|
||||
RedisElementReader<T> getReader();
|
||||
|
||||
/**
|
||||
* Deserialize a {@link ByteBuffer} into the according type.
|
||||
*
|
||||
* @param buffer must not be {@literal null}.
|
||||
* @return the deserialized value.
|
||||
*/
|
||||
default T read(ByteBuffer buffer) {
|
||||
return getReader().read(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link RedisElementWriter}.
|
||||
*/
|
||||
RedisElementWriter<T> getWriter();
|
||||
|
||||
/**
|
||||
* Serialize a {@code element} to its {@link ByteBuffer} representation.
|
||||
*
|
||||
* @param element
|
||||
* @return the {@link ByteBuffer} representing {@code element} in its binary form.
|
||||
*/
|
||||
default ByteBuffer write(T element) {
|
||||
return getWriter().write(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link ReactiveSerializationContext}.
|
||||
*/
|
||||
interface ReactiveSerializationContextBuilder<K, V> {
|
||||
|
||||
/**
|
||||
* Set the key {@link SerializationTuple}.
|
||||
*
|
||||
* @param tuple must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> key(SerializationTuple<K> tuple);
|
||||
|
||||
/**
|
||||
* Set the key {@link RedisElementReader} and {@link RedisElementWriter}.
|
||||
*
|
||||
* @param reader must not be {@literal null}.
|
||||
* @param writer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> key(RedisElementReader<K> reader, RedisElementWriter<K> writer);
|
||||
|
||||
/**
|
||||
* Set the key {@link SerializationTuple} given a {@link RedisSerializer}.
|
||||
*
|
||||
* @param serializer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> key(RedisSerializer<K> serializer);
|
||||
|
||||
/**
|
||||
* Set the value {@link SerializationTuple}.
|
||||
*
|
||||
* @param tuple must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> value(SerializationTuple<V> tuple);
|
||||
|
||||
/**
|
||||
* Set the value {@link RedisElementReader} and {@link RedisElementWriter}.
|
||||
*
|
||||
* @param reader must not be {@literal null}.
|
||||
* @param writer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> value(RedisElementReader<V> reader, RedisElementWriter<V> writer);
|
||||
|
||||
/**
|
||||
* Set the value {@link SerializationTuple} given a {@link RedisSerializer}.
|
||||
*
|
||||
* @param serializer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> value(RedisSerializer<V> serializer);
|
||||
|
||||
/**
|
||||
* Set the hash key {@link SerializationTuple}.
|
||||
*
|
||||
* @param tuple must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> hashKey(SerializationTuple<?> tuple);
|
||||
|
||||
/**
|
||||
* Set the hash key {@link RedisElementReader} and {@link RedisElementWriter}.
|
||||
*
|
||||
* @param reader must not be {@literal null}.
|
||||
* @param writer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> hashKey(RedisElementReader<? extends Object> reader,
|
||||
RedisElementWriter<? extends Object> writer);
|
||||
|
||||
/**
|
||||
* Set the hash key {@link SerializationTuple} given a {@link RedisSerializer}.
|
||||
*
|
||||
* @param serializer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> hashKey(RedisSerializer<? extends Object> serializer);
|
||||
|
||||
/**
|
||||
* Set the hash value {@link SerializationTuple}.
|
||||
*
|
||||
* @param tuple must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> hashValue(SerializationTuple<?> tuple);
|
||||
|
||||
/**
|
||||
* Set the hash value {@link RedisElementReader} and {@link RedisElementWriter}.
|
||||
*
|
||||
* @param reader must not be {@literal null}.
|
||||
* @param writer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> hashValue(RedisElementReader<? extends Object> reader,
|
||||
RedisElementWriter<? extends Object> writer);
|
||||
|
||||
/**
|
||||
* Set the hash value {@link SerializationTuple} given a {@link RedisSerializer}.
|
||||
*
|
||||
* @param serializer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> hashValue(RedisSerializer<? extends Object> serializer);
|
||||
|
||||
/**
|
||||
* Set the string {@link SerializationTuple}.
|
||||
*
|
||||
* @param tuple must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> string(SerializationTuple<String> tuple);
|
||||
|
||||
/**
|
||||
* Set the string {@link RedisElementReader} and {@link RedisElementWriter}.
|
||||
*
|
||||
* @param reader must not be {@literal null}.
|
||||
* @param writer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> string(RedisElementReader<String> reader,
|
||||
RedisElementWriter<String> writer);
|
||||
|
||||
/**
|
||||
* Set the string {@link SerializationTuple} given a {@link RedisSerializer}.
|
||||
*
|
||||
* @param serializer must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
*/
|
||||
ReactiveSerializationContextBuilder<K, V> string(RedisSerializer<String> serializer);
|
||||
|
||||
/**
|
||||
* Builds a {@link ReactiveSerializationContext}.
|
||||
*
|
||||
* @return the {@link ReactiveSerializationContext}.
|
||||
*/
|
||||
ReactiveSerializationContext<K, V> build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.serializer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Strategy interface that specifies a deserializer that can deserialize a binary element representation stored in Redis
|
||||
* into an object.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RedisElementReader<T> {
|
||||
|
||||
/**
|
||||
* Deserialize a {@link ByteBuffer} into the according type.
|
||||
*
|
||||
* @param buffer must not be {@literal null}.
|
||||
* @return the deserialized value.
|
||||
*/
|
||||
T read(ByteBuffer buffer);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.serializer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Strategy interface that specifies a serializer that can serialize an element to its binary representation to be used
|
||||
* as Redis protocol payload.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RedisElementWriter<T> {
|
||||
|
||||
/**
|
||||
* Serialize a {@code element} to its {@link ByteBuffer} representation.
|
||||
*
|
||||
* @param element
|
||||
* @return the {@link ByteBuffer} representing {@code element} in its binary form.
|
||||
*/
|
||||
ByteBuffer write(T element);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2017 the original author 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.serializer;
|
||||
|
||||
import org.springframework.data.redis.serializer.ReactiveSerializationContext.SerializationTuple;
|
||||
|
||||
/**
|
||||
* Adapter to delegate serialization/deserialization to {@link RedisSerializer}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
class RedisSerializerTupleAdapter<T> implements SerializationTuple<T> {
|
||||
|
||||
private final static RedisSerializerTupleAdapter<?> RAW = new RedisSerializerTupleAdapter<>(null);
|
||||
|
||||
private final RedisElementReader<T> reader;
|
||||
private final RedisElementWriter<T> writer;
|
||||
|
||||
protected RedisSerializerTupleAdapter(RedisSerializer<T> serializer) {
|
||||
|
||||
reader = new DefaultRedisElementReader<>(serializer);
|
||||
writer = new DefaultRedisElementWriter<>(serializer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> SerializationTuple<T> raw() {
|
||||
return (SerializationTuple) RAW;
|
||||
}
|
||||
|
||||
public static <T> SerializationTuple<T> from(RedisSerializer<T> redisSerializer) {
|
||||
return new RedisSerializerTupleAdapter<>(redisSerializer);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.serializer.ReactiveSerializationContext.SerializationTuple#reader()
|
||||
*/
|
||||
@Override
|
||||
public RedisElementReader<T> getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.serializer.ReactiveSerializationContext.SerializationTuple#writer()
|
||||
*/
|
||||
@Override
|
||||
public RedisElementWriter<T> getWriter() {
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user