DATAREDIS-683 - Polishing.
Rename *Operators -> *Executor and use plain RedisElementReader/Writer instead of SerilaizationPairs. Therefore we’ve introduced new static factory methods creating the required Reader/Writer from a given RedisSerializer. Additionally some minor Javadoc updates, tests and removal of redundant null checks. Original Pull Request: #268
This commit is contained in:
@@ -19,11 +19,16 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Redis Scripting commands executed using reactive infrastructure.
|
||||
* Redis <a href="https://redis.io/commands/#scripting">Scripting</a> commands executed using reactive infrastructure.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveScriptingCommands {
|
||||
@@ -47,19 +52,31 @@ public interface ReactiveScriptingCommands {
|
||||
* Execute the script by calling {@link #evalSha(String, ReturnType, int, ByteBuffer...)}.
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @return
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/script-load">Redis Documentation: SCRIPT LOAD</a>
|
||||
*/
|
||||
Mono<String> scriptLoad(ByteBuffer script);
|
||||
|
||||
/**
|
||||
* Check if given {@code scriptSha} exist in script cache.
|
||||
*
|
||||
* @param scriptSha The sha1 of the script is present in script cache. Must not be {@literal null}.
|
||||
* @return a {@link Mono} indicating if script is present.
|
||||
*/
|
||||
default Mono<Boolean> scriptExists(String scriptSha) {
|
||||
|
||||
Assert.notNull(scriptSha, "ScriptSha must not be null!");
|
||||
return scriptExists(Collections.singletonList(scriptSha)).singleOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given {@code scriptShas} exist in script cache.
|
||||
*
|
||||
* @param scriptShas
|
||||
* @return one entry per given scriptSha in returned list.
|
||||
* @param scriptShas must not be {@literal null}.
|
||||
* @return {@link Flux} emitting one entry per scriptSha in given {@link List}.
|
||||
* @see <a href="http://redis.io/commands/script-exists">Redis Documentation: SCRIPT EXISTS</a>
|
||||
*/
|
||||
Flux<Boolean> scriptExists(String... scriptShas);
|
||||
Flux<Boolean> scriptExists(List<String> scriptShas);
|
||||
|
||||
/**
|
||||
* Evaluate given {@code script}.
|
||||
@@ -68,7 +85,7 @@ public interface ReactiveScriptingCommands {
|
||||
* @param returnType must not be {@literal null}.
|
||||
* @param numKeys
|
||||
* @param keysAndArgs must not be {@literal null}.
|
||||
* @return
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/eval">Redis Documentation: EVAL</a>
|
||||
*/
|
||||
<T> Flux<T> eval(ByteBuffer script, ReturnType returnType, int numKeys, ByteBuffer... keysAndArgs);
|
||||
@@ -80,7 +97,7 @@ public interface ReactiveScriptingCommands {
|
||||
* @param returnType must not be {@literal null}.
|
||||
* @param numKeys
|
||||
* @param keysAndArgs must not be {@literal null}.
|
||||
* @return
|
||||
* @return never {@literal null}.
|
||||
* @see <a href="http://redis.io/commands/evalsha">Redis Documentation: EVALSHA</a>
|
||||
*/
|
||||
<T> Flux<T> evalSha(String scriptSha, ReturnType returnType, int numKeys, ByteBuffer... keysAndArgs);
|
||||
|
||||
@@ -22,13 +22,17 @@ import reactor.core.publisher.Mono;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.redis.connection.ReactiveScriptingCommands;
|
||||
import org.springframework.data.redis.connection.ReturnType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ReactiveScriptingCommands} implementation for the <a href="https://lettuce.io/">Lettuce</a> Redis driver.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
class LettuceReactiveScriptingCommands implements ReactiveScriptingCommands {
|
||||
@@ -81,14 +85,14 @@ class LettuceReactiveScriptingCommands implements ReactiveScriptingCommands {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveScriptingCommands#scriptExists(java.lang.String[])
|
||||
* @see org.springframework.data.redis.connection.ReactiveScriptingCommands#scriptExists(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public Flux<Boolean> scriptExists(String... scriptShas) {
|
||||
public Flux<Boolean> scriptExists(List<String> scriptShas) {
|
||||
|
||||
Assert.notNull(scriptShas, "Script SHAs must not be null!");
|
||||
Assert.notEmpty(scriptShas, "Script SHAs must not be empty!");
|
||||
|
||||
return connection.execute(cmd -> cmd.scriptExists(scriptShas));
|
||||
return connection.execute(cmd -> cmd.scriptExists(scriptShas.toArray(new String[scriptShas.size()])));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -20,13 +20,15 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.core.script.RedisScript;
|
||||
import org.springframework.data.redis.serializer.RedisElementReader;
|
||||
import org.springframework.data.redis.serializer.RedisElementWriter;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
|
||||
/**
|
||||
@@ -181,31 +183,54 @@ public interface ReactiveRedisOperations<K, V> {
|
||||
// Methods dealing with Redis Lua scripts
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Executes the given {@link RedisScript}.
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @return result value of the script {@link Flux#empty()} if {@link RedisScript#getResultType()} is {@literal null},
|
||||
* likely indicating a throw-away status reply (i.e. "OK").
|
||||
*/
|
||||
default <T> Flux<T> execute(RedisScript<T> script) {
|
||||
return execute(script, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given {@link RedisScript}.
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return result value of the script {@link Flux#empty()} if {@link RedisScript#getResultType()} is {@literal null},
|
||||
* likely indicating a throw-away status reply (i.e. "OK").
|
||||
*/
|
||||
default <T> Flux<T> execute(RedisScript<T> script, List<K> keys) {
|
||||
return execute(script, keys, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given {@link RedisScript}
|
||||
*
|
||||
* @param script The script to execute
|
||||
* @param keys keys that need to be passed to the script.
|
||||
* @param args args that need to be passed to the script.
|
||||
* @return return value of the script or raw {@link java.nio.ByteBuffer} if {@link RedisScript#getResultType()} is
|
||||
* {@literal null}, likely indicating a throw-away status reply (i.e. "OK").
|
||||
* @param script The script to execute. Must not be {@literal null}.
|
||||
* @param keys keys that need to be passed to the script. Must not be {@literal null}.
|
||||
* @param args args that need to be passed to the script. Must not be {@literal null}.
|
||||
* @return result value of the script {@link Flux#empty()} if {@link RedisScript#getResultType()} is {@literal null},
|
||||
* likely indicating a throw-away status reply (i.e. "OK").
|
||||
*/
|
||||
<T> Flux<T> execute(RedisScript<T> script, List<K> keys, Object... args);
|
||||
<T> Flux<T> execute(RedisScript<T> script, List<K> keys, List<?> args);
|
||||
|
||||
/**
|
||||
* Executes the given {@link RedisScript}, using the provided {@link RedisSerializer}s to serialize the script
|
||||
* arguments and result.
|
||||
*
|
||||
* @param script The script to execute
|
||||
* @param argsSerializerPair The {@link SerializationPair} to use for serializing args
|
||||
* @param resultSerializerPair The {@link SerializationPair} to use for serializing the script return value
|
||||
* @param argsWriter The {@link RedisElementWriter} to use for serializing args
|
||||
* @param resultReader The {@link RedisElementReader} to use for serializing the script return value
|
||||
* @param keys keys that need to be passed to the script.
|
||||
* @param args args that need to be passed to the script.
|
||||
* @return return value of the script or raw {@link java.nio.ByteBuffer} if {@link RedisScript#getResultType()} is
|
||||
* {@literal null}, likely indicating a throw-away status reply (i.e. "OK").
|
||||
* @return result value of the script {@link Flux#empty()} if {@link RedisScript#getResultType()} is {@literal null},
|
||||
* likely indicating a throw-away status reply (i.e. "OK").
|
||||
*/
|
||||
<T> Flux<T> execute(RedisScript<T> script, SerializationPair<?> argsSerializerPair,
|
||||
SerializationPair<T> resultSerializerPair, List<K> keys, Object... args);
|
||||
<T> Flux<T> execute(RedisScript<T> script, List<K> keys, List<?> args, RedisElementWriter<?> argsWriter,
|
||||
RedisElementReader<T> resultReader);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods to obtain specific operations interface objects.
|
||||
|
||||
@@ -30,11 +30,12 @@ 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.core.script.DefaultScriptOperators;
|
||||
import org.springframework.data.redis.core.script.DefaultReactiveScriptExecutor;
|
||||
import org.springframework.data.redis.core.script.ReactiveScriptExecutor;
|
||||
import org.springframework.data.redis.core.script.RedisScript;
|
||||
import org.springframework.data.redis.core.script.ScriptOperators;
|
||||
import org.springframework.data.redis.serializer.RedisElementReader;
|
||||
import org.springframework.data.redis.serializer.RedisElementWriter;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -58,7 +59,7 @@ public class ReactiveRedisTemplate<K, V> implements ReactiveRedisOperations<K, V
|
||||
private final ReactiveRedisConnectionFactory connectionFactory;
|
||||
private final RedisSerializationContext<K, V> serializationContext;
|
||||
private final boolean exposeConnection;
|
||||
private final ScriptOperators<K> scriptOperators;
|
||||
private final ReactiveScriptExecutor<K> reactiveScriptExecutor;
|
||||
|
||||
/**
|
||||
* Creates new {@link ReactiveRedisTemplate} using given {@link ReactiveRedisConnectionFactory} and
|
||||
@@ -89,7 +90,7 @@ public class ReactiveRedisTemplate<K, V> implements ReactiveRedisOperations<K, V
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.serializationContext = serializationContext;
|
||||
this.exposeConnection = exposeConnection;
|
||||
this.scriptOperators = new DefaultScriptOperators<>(connectionFactory, serializationContext);
|
||||
this.reactiveScriptExecutor = new DefaultReactiveScriptExecutor<>(connectionFactory, serializationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -491,21 +492,23 @@ public class ReactiveRedisTemplate<K, V> implements ReactiveRedisOperations<K, V
|
||||
// Methods dealing with Redis Lua scripts
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#execute(org.springframework.data.redis.core.script.RedisScript, java.util.List, java.lang.Object[])
|
||||
/*
|
||||
*(non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#execute(org.springframework.data.redis.core.script.RedisScript, java.util.List, java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> execute(RedisScript<T> script, List<K> keys, Object... args) {
|
||||
return scriptOperators.execute(script, keys, args);
|
||||
public <T> Flux<T> execute(RedisScript<T> script, List<K> keys, List<?> args) {
|
||||
return reactiveScriptExecutor.execute(script, keys, args);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#execute(org.springframework.data.redis.core.script.RedisScript, org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair, org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair, java.util.List, java.lang.Object[])
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveRedisOperations#execute(org.springframework.data.redis.core.script.RedisScript, java.util.List, java.util.List, org.springframework.data.redis.serializer.RedisElementWriter, org.springframework.data.redis.serializer.RedisElementReader)
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> execute(RedisScript<T> script, SerializationPair<?> argsSerializerPair,
|
||||
SerializationPair<T> resultSerializerPair, List<K> keys, Object... args) {
|
||||
return scriptOperators.execute(script, argsSerializerPair, resultSerializerPair, keys, args);
|
||||
public <T> Flux<T> execute(RedisScript<T> script, List<K> keys, List<?> args, RedisElementWriter<?> argsWriter,
|
||||
RedisElementReader<T> resultReader) {
|
||||
return reactiveScriptExecutor.execute(script, keys, args, argsWriter, resultReader);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -19,37 +19,41 @@ import reactor.core.publisher.Flux;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.redis.RedisSystemException;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.ReturnType;
|
||||
import org.springframework.data.redis.core.ReactiveRedisCallback;
|
||||
import org.springframework.data.redis.serializer.RedisElementReader;
|
||||
import org.springframework.data.redis.serializer.RedisElementWriter;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ScriptOperators}. Optimizes performance by attempting to execute script first using
|
||||
* evalsha, then falling back to eval if Redis has not yet cached the script.
|
||||
* Default implementation of {@link ReactiveScriptExecutor}. Optimizes performance by attempting to execute script first
|
||||
* using {@code EVALSHA}, then falling back to {@code EVAL} if Redis has not yet cached the script.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @param <K> The type of keys that may be passed during script execution
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultScriptOperators<K> implements ScriptOperators<K> {
|
||||
public class DefaultReactiveScriptExecutor<K> implements ReactiveScriptExecutor<K> {
|
||||
|
||||
private final ReactiveRedisConnectionFactory connectionFactory;
|
||||
private final RedisSerializationContext<K, ?> serializationContext;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultScriptOperators} given {@link ReactiveRedisConnectionFactory} and
|
||||
* Creates a new {@link DefaultReactiveScriptExecutor} given {@link ReactiveRedisConnectionFactory} and
|
||||
* {@link RedisSerializationContext}.
|
||||
*
|
||||
* @param connectionFactory must not be {@literal null}.
|
||||
* @param serializationContext must not be {@literal null}.
|
||||
*/
|
||||
public DefaultScriptOperators(ReactiveRedisConnectionFactory connectionFactory,
|
||||
public DefaultReactiveScriptExecutor(ReactiveRedisConnectionFactory connectionFactory,
|
||||
RedisSerializationContext<K, ?> serializationContext) {
|
||||
|
||||
Assert.notNull(connectionFactory, "ReactiveRedisConnectionFactory must not be null!");
|
||||
@@ -59,49 +63,51 @@ public class DefaultScriptOperators<K> implements ScriptOperators<K> {
|
||||
this.serializationContext = serializationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.script.ScriptOperators#execute(org.springframework.data.redis.core.script.RedisScript, java.util.List, java.lang.Object[])
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.script.ReactiveScriptExecutor#execute(org.springframework.data.redis.core.script.RedisScript, java.util.List, java.util.List)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Flux<T> execute(RedisScript<T> script, List<K> keys, Object... args) {
|
||||
public <T> Flux<T> execute(RedisScript<T> script, List<K> keys, List<?> args) {
|
||||
|
||||
Assert.notNull(script, "RedisScript must not be null!");
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
Assert.notNull(args, "Args must not be null!");
|
||||
|
||||
// use the Template's value serializer for args and result
|
||||
return execute(script, serializationContext.getKeySerializationPair(),
|
||||
(SerializationPair<T>) serializationContext.getValueSerializationPair(), keys, args);
|
||||
return execute(script, keys, args, serializationContext.getKeySerializationPair().getWriter(),
|
||||
(RedisElementReader<T>) serializationContext.getValueSerializationPair().getReader());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.script.ScriptOperators#execute(org.springframework.data.redis.core.script.RedisScript, org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair, org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair, java.util.List, java.lang.Object[])
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.script.ReactiveScriptExecutor#execute(org.springframework.data.redis.core.script.RedisScript, java.util.List, java.util.List, org.springframework.data.redis.serializer.RedisElementWriter, org.springframework.data.redis.serializer.RedisElementReader)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Flux<T> execute(RedisScript<T> script, SerializationPair<?> argsSerializerPair,
|
||||
SerializationPair<T> resultSerializationPair, List<K> keys, Object... args) {
|
||||
public <T> Flux<T> execute(RedisScript<T> script, List<K> keys, List<?> args, RedisElementWriter<?> argsWriter,
|
||||
RedisElementReader<T> resultReader) {
|
||||
|
||||
Assert.notNull(script, "RedisScript must not be null!");
|
||||
Assert.notNull(argsSerializerPair, "Argument SerializationPair must not be null!");
|
||||
Assert.notNull(resultSerializationPair, "Result SerializationPair must not be null!");
|
||||
Assert.notNull(argsWriter, "Argument Writer must not be null!");
|
||||
Assert.notNull(resultReader, "Result Reader must not be null!");
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
Assert.notNull(args, "Args must not be null!");
|
||||
|
||||
return execute(connection -> {
|
||||
|
||||
ReturnType returnType = ReturnType.fromJavaType(script.getResultType());
|
||||
ByteBuffer[] keysAndArgs = keysAndArgs((SerializationPair<Object>) argsSerializerPair, keys, args);
|
||||
ByteBuffer[] keysAndArgs = keysAndArgs(argsWriter, keys, args);
|
||||
int keySize = keys.size();
|
||||
|
||||
return eval(connection, script, returnType, keySize, keysAndArgs, resultSerializationPair);
|
||||
return eval(connection, script, returnType, keySize, keysAndArgs, resultReader);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
protected <T> Flux<T> eval(ReactiveRedisConnection connection, RedisScript<T> script, ReturnType returnType,
|
||||
int numKeys, ByteBuffer[] keysAndArgs, SerializationPair<T> resultSerializer) {
|
||||
int numKeys, ByteBuffer[] keysAndArgs, RedisElementReader<T> resultReader) {
|
||||
|
||||
Flux<T> result = connection.scriptingCommands().evalSha(script.getSha1(), returnType, numKeys, keysAndArgs);
|
||||
|
||||
@@ -115,41 +121,25 @@ public class DefaultScriptOperators<K> implements ScriptOperators<K> {
|
||||
.error(e instanceof RuntimeException ? (RuntimeException) e : new RedisSystemException(e.getMessage(), e));
|
||||
});
|
||||
|
||||
return script.getResultType() == null ? result : deserializeResult(resultSerializer, result);
|
||||
return script.returnsRawValue() ? result : deserializeResult(resultReader, result);
|
||||
}
|
||||
|
||||
protected ByteBuffer[] keysAndArgs(SerializationPair<Object> argsSerializer, List<K> keys, Object[] args) {
|
||||
protected ByteBuffer[] keysAndArgs(RedisElementWriter argsWriter, List<K> keys, List<?> args) {
|
||||
|
||||
int keySize = keys != null ? keys.size() : 0;
|
||||
ByteBuffer[] keysAndArgs = new ByteBuffer[args.length + keySize];
|
||||
int i = 0;
|
||||
|
||||
if (keys != null) {
|
||||
for (K key : keys) {
|
||||
if (key instanceof ByteBuffer) {
|
||||
keysAndArgs[i++] = (ByteBuffer) key;
|
||||
} else {
|
||||
keysAndArgs[i++] = keySerializer().getWriter().write(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Object arg : args) {
|
||||
if (arg instanceof ByteBuffer) {
|
||||
keysAndArgs[i++] = (ByteBuffer) arg;
|
||||
} else {
|
||||
keysAndArgs[i++] = argsSerializer.getWriter().write(arg);
|
||||
}
|
||||
}
|
||||
return keysAndArgs;
|
||||
return Stream.concat(keys.stream().map(t -> keySerializer().getWriter().write(t)),
|
||||
args.stream().map(t -> argsWriter.write(t))).toArray(size -> new ByteBuffer[size]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param script
|
||||
* @return
|
||||
*/
|
||||
protected ByteBuffer scriptBytes(RedisScript<?> script) {
|
||||
return serializationContext.getStringSerializationPair().getWriter().write(script.getScriptAsString());
|
||||
}
|
||||
|
||||
protected <T> Flux<T> deserializeResult(SerializationPair<T> pair, Flux<T> result) {
|
||||
return result.map(it -> ScriptUtils.deserializeResult(pair.getReader(), it));
|
||||
protected <T> Flux<T> deserializeResult(RedisElementReader<T> reader, Flux<T> result) {
|
||||
return result.map(it -> ScriptUtils.deserializeResult(reader, it));
|
||||
}
|
||||
|
||||
protected SerializationPair<K> keySerializer() {
|
||||
@@ -174,6 +164,7 @@ public class DefaultScriptOperators<K> implements ScriptOperators<K> {
|
||||
try {
|
||||
return Flux.defer(() -> action.doInRedis(conn)).doFinally(signal -> conn.close());
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
conn.close();
|
||||
throw e;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -37,23 +37,32 @@ import org.springframework.util.Assert;
|
||||
public class DefaultRedisScript<T> implements RedisScript<T>, InitializingBean {
|
||||
|
||||
private ScriptSource scriptSource;
|
||||
|
||||
private String sha1;
|
||||
|
||||
private Class<T> resultType;
|
||||
|
||||
private final Object shaModifiedMonitor = new Object();
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultRedisScript}
|
||||
*/
|
||||
public DefaultRedisScript() {}
|
||||
public DefaultRedisScript() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultRedisScript}
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @since 2.0
|
||||
*/
|
||||
public DefaultRedisScript(String script) {
|
||||
this(script, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultRedisScript}
|
||||
*
|
||||
* @param script
|
||||
* @param resultType
|
||||
* @param script must not be {@literal null}.
|
||||
* @param resultType can be {@literal null}.
|
||||
*/
|
||||
public DefaultRedisScript(String script, Class<T> resultType) {
|
||||
|
||||
@@ -61,11 +70,20 @@ public class DefaultRedisScript<T> implements RedisScript<T>, InitializingBean {
|
||||
this.resultType = resultType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.scriptSource, "Either script, script location," + " or script source is required");
|
||||
Assert.state(this.scriptSource != null, "Either script, script location," + " or script source is required");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.script.RedisScript#getSha1()
|
||||
*/
|
||||
public String getSha1() {
|
||||
|
||||
synchronized (shaModifiedMonitor) {
|
||||
if (sha1 == null || scriptSource.isModified()) {
|
||||
this.sha1 = DigestUtils.sha1DigestAsHex(getScriptAsString());
|
||||
@@ -74,11 +92,20 @@ public class DefaultRedisScript<T> implements RedisScript<T>, InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.script.RedisScript#getResultType()
|
||||
*/
|
||||
public Class<T> getResultType() {
|
||||
return this.resultType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.script.RedisScript#getScriptAsString()
|
||||
*/
|
||||
public String getScriptAsString() {
|
||||
|
||||
try {
|
||||
return scriptSource.getScriptAsString();
|
||||
} catch (IOException e) {
|
||||
@@ -95,7 +122,7 @@ public class DefaultRedisScript<T> implements RedisScript<T>, InitializingBean {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param script The script text
|
||||
* @param scriptText The script text
|
||||
*/
|
||||
public void setScriptText(String scriptText) {
|
||||
this.scriptSource = new StaticScriptSource(scriptText);
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.data.redis.core.script;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
import org.springframework.data.redis.RedisSystemException;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.ReturnType;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.script;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.redis.serializer.RedisElementReader;
|
||||
import org.springframework.data.redis.serializer.RedisElementWriter;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
|
||||
/**
|
||||
* Executes {@link RedisScript}s using reactive infrastructure.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @param <K> The type of keys that may be passed during script execution
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveScriptExecutor<K> {
|
||||
|
||||
/**
|
||||
* Execute the given {@link RedisScript}
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @return the return value of the script or {@link Flux#empty()} if {@link RedisScript#getResultType()} is
|
||||
* {@literal null}, likely indicating a throw-away status reply (i.e. "OK")
|
||||
*/
|
||||
default <T> Flux<T> execute(RedisScript<T> script) {
|
||||
return execute(script, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link RedisScript}
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return the return value of the script or {@link Flux#empty()} if {@link RedisScript#getResultType()} is
|
||||
* {@literal null}, likely indicating a throw-away status reply (i.e. "OK")
|
||||
*/
|
||||
default <T> Flux<T> execute(RedisScript<T> script, List<K> keys) {
|
||||
return execute(script, keys, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given {@link RedisScript}
|
||||
*
|
||||
* @param script The script to execute. Must not be {@literal null}.
|
||||
* @param keys Any keys that need to be passed to the script. Must not be {@literal null}.
|
||||
* @param args Any args that need to be passed to the script. Can be {@literal empty}.
|
||||
* @return The return value of the script or {@link Flux#empty()} if {@link RedisScript#getResultType()} is
|
||||
* {@literal null}, likely indicating a throw-away status reply (i.e. "OK")
|
||||
*/
|
||||
<T> Flux<T> execute(RedisScript<T> script, List<K> keys, List<?> args);
|
||||
|
||||
/**
|
||||
* Executes the given {@link RedisScript}, using the provided {@link RedisSerializer}s to serialize the script
|
||||
* arguments and result.
|
||||
*
|
||||
* @param script The script to execute. must not be {@literal null}.
|
||||
* @param keys Any keys that need to be passed to the script
|
||||
* @param args Any args that need to be passed to the script
|
||||
* @param argsWriter The {@link RedisElementWriter} to use for serializing args. Must not be {@literal null}.
|
||||
* @param resultReader The {@link RedisElementReader} to use for serializing the script return value. Must not be
|
||||
* {@literal null}.
|
||||
* @return The return value of the script or {@link Flux#empty()} if {@link RedisScript#getResultType()} is
|
||||
* {@literal null}, likely indicating a throw-away status reply (i.e. "OK")
|
||||
*/
|
||||
<T> Flux<T> execute(RedisScript<T> script, List<K> keys, List<?> args, RedisElementWriter<?> argsWriter,
|
||||
RedisElementReader<T> resultReader);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-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,30 +15,67 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core.script;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A script to be executed using the <a href="http://redis.io/commands/eval">Redis scripting support</a> available as of
|
||||
* version 2.6
|
||||
*
|
||||
* @author Jennifer Hickey
|
||||
* @param <T> The script result type. Should be one of Long, Boolean, List, or deserialized value type. Can be null if
|
||||
* the script returns a throw-away status (i.e "OK")
|
||||
* @author Christoph Strobl
|
||||
* @param <T> The script result type. Should be one of Long, Boolean, List, or deserialized value type. Can be
|
||||
* {@litearl null} if the script returns a throw-away status (i.e "OK")
|
||||
*/
|
||||
public interface RedisScript<T> {
|
||||
|
||||
/**
|
||||
* @return The SHA1 of the script, used for executing Redis evalsha command
|
||||
* @return The SHA1 of the script, used for executing Redis evalsha command.
|
||||
*/
|
||||
String getSha1();
|
||||
|
||||
/**
|
||||
* @return The script result type. Should be one of Long, Boolean, List, or deserialized value type. Can be null if
|
||||
* the script returns a throw-away status (i.e "OK")
|
||||
* @return The script result type. Should be one of Long, Boolean, List, or deserialized value type. {@literal null}
|
||||
* if the script returns a throw-away status (i.e "OK").
|
||||
*/
|
||||
Class<T> getResultType();
|
||||
|
||||
/**
|
||||
* @return The script contents
|
||||
* @return The script contents.
|
||||
*/
|
||||
String getScriptAsString();
|
||||
|
||||
/**
|
||||
* @return {@literal true} if result type is {@literal null} and does not need any further deserialization.
|
||||
* @since 2.0
|
||||
*/
|
||||
default boolean returnsRawValue() {
|
||||
return getResultType() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisScript} from {@link String}.
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @return new instance of {@link RedisScript}.
|
||||
* @since 2.0
|
||||
*/
|
||||
static <T> RedisScript<T> of(String script) {
|
||||
return new DefaultRedisScript<>(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisScript} from {@link String}.
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @param resultType must not be {@literal null}.
|
||||
* @return new instance of {@link RedisScript}.
|
||||
* @since 2.0
|
||||
*/
|
||||
static <T> RedisScript of(String script, Class<T> resultType) {
|
||||
|
||||
Assert.notNull(script, "Script must not be null!");
|
||||
Assert.notNull(resultType, "ResultType must not be null!");
|
||||
|
||||
return new DefaultRedisScript(script, resultType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* 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.script;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
|
||||
/**
|
||||
* Executes {@link RedisScript}s using reactive infrastructure.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @param <K> The type of keys that may be passed during script execution
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ScriptOperators<K> {
|
||||
|
||||
/**
|
||||
* Executes the given {@link RedisScript}
|
||||
*
|
||||
* @param script The script to execute
|
||||
* @param keys Any keys that need to be passed to the script
|
||||
* @param args Any args that need to be passed to the script
|
||||
* @return The return value of the script or null if {@link RedisScript#getResultType()} is null, likely indicating a
|
||||
* throw-away status reply (i.e. "OK")
|
||||
*/
|
||||
<T> Flux<T> execute(RedisScript<T> script, List<K> keys, Object... args);
|
||||
|
||||
/**
|
||||
* Executes the given {@link RedisScript}, using the provided {@link RedisSerializer}s to serialize the script
|
||||
* arguments and result.
|
||||
*
|
||||
* @param script The script to execute
|
||||
* @param argsSerializer The {@link SerializationPair} to use for serializing args
|
||||
* @param resultSerializer The {@link SerializationPair} to use for serializing the script return value
|
||||
* @param keys Any keys that need to be passed to the script
|
||||
* @param args Any args that need to be passed to the script
|
||||
* @return The return value of the script or null if {@link RedisScript#getResultType()} is null, likely indicating a
|
||||
* throw-away status reply (i.e. "OK")
|
||||
*/
|
||||
<T> Flux<T> execute(RedisScript<T> script, SerializationPair<?> argsSerializer, SerializationPair<T> resultSerializer,
|
||||
List<K> keys, Object... args);
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
* Utilities for Lua script execution and result deserialization.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
class ScriptUtils {
|
||||
@@ -45,7 +46,7 @@ class ScriptUtils {
|
||||
static <T> T deserializeResult(RedisSerializer<T> resultSerializer, Object result) {
|
||||
|
||||
if (result instanceof byte[]) {
|
||||
return resultSerializer == null ? (T) result : resultSerializer.deserialize((byte[]) result);
|
||||
return resultSerializer.deserialize((byte[]) result);
|
||||
}
|
||||
|
||||
if (result instanceof List) {
|
||||
@@ -66,7 +67,7 @@ class ScriptUtils {
|
||||
* Deserialize {@code result} using {@link RedisElementReader} to the reader type. Collection types and intermediate
|
||||
* collection elements are deserialized recursivly.
|
||||
*
|
||||
* @param resultSerializer must not be {@literal null}.
|
||||
* @param reader must not be {@literal null}.
|
||||
* @param result must not be {@literal null}.
|
||||
* @return the deserialized result.
|
||||
*/
|
||||
@@ -75,7 +76,7 @@ class ScriptUtils {
|
||||
|
||||
if (result instanceof ByteBuffer) {
|
||||
|
||||
return reader == null ? (T) result : reader.read((ByteBuffer) result);
|
||||
return reader.read((ByteBuffer) result);
|
||||
}
|
||||
|
||||
if (result instanceof List) {
|
||||
|
||||
@@ -17,11 +17,14 @@ package org.springframework.data.redis.serializer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Strategy interface that specifies a deserializer that can deserialize a binary element representation stored in Redis
|
||||
* into an object.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@@ -34,4 +37,16 @@ public interface RedisElementReader<T> {
|
||||
* @return the deserialized value.
|
||||
*/
|
||||
T read(ByteBuffer buffer);
|
||||
|
||||
/**
|
||||
* Create new {@link RedisElementReader} using given {@link RedisSerializer}.
|
||||
*
|
||||
* @param serializer must not be {@literal null}.
|
||||
* @return new instance of {@link RedisElementReader}.
|
||||
*/
|
||||
static <T> RedisElementReader<T> from(RedisSerializer<T> serializer) {
|
||||
|
||||
Assert.notNull(serializer, "Serializer must not be null!");
|
||||
return new DefaultRedisElementReader<>(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,15 @@ package org.springframework.data.redis.serializer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RedisElementWriter<T> {
|
||||
@@ -29,8 +33,20 @@ public interface RedisElementWriter<T> {
|
||||
/**
|
||||
* Serialize a {@code element} to its {@link ByteBuffer} representation.
|
||||
*
|
||||
* @param element
|
||||
* @param element can be {@literal null}.
|
||||
* @return the {@link ByteBuffer} representing {@code element} in its binary form.
|
||||
*/
|
||||
ByteBuffer write(T element);
|
||||
|
||||
/**
|
||||
* Create new {@link RedisElementWriter} using given {@link RedisSerializer}.
|
||||
*
|
||||
* @param serializer must not be {@literal null}.
|
||||
* @return new instance of {@link RedisElementWriter}.
|
||||
*/
|
||||
static <T> RedisElementWriter<T> from(RedisSerializer<T> serializer) {
|
||||
|
||||
Assert.notNull(serializer, "Serializer must not be null!");
|
||||
return new DefaultRedisElementWriter<>(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user