/* * 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. * 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 reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.nio.ByteBuffer; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.reactivestreams.Publisher; import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.Command; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand; import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.RangeCommand; import org.springframework.data.redis.connection.RedisListCommands.Position; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** * Redis List commands executed using reactive infrastructure. * * @author Christoph Strobl * @author Mark Paluch * @since 2.0 */ public interface ReactiveListCommands { /** * @author Christoph Strobl */ enum Direction { LEFT, RIGHT } /** * {@code LPUSH}/{@literal RPUSH} command parameters. * * @author Christoph Strobl * @see <a href="http://redis.io/commands/lpush">Redis Documentation: LPUSH</a> * @see <a href="http://redis.io/commands/rpush">Redis Documentation: RPUSH</a> */ class PushCommand extends KeyCommand { private List<ByteBuffer> values; private boolean upsert; private Direction direction; private PushCommand(ByteBuffer key, List<ByteBuffer> values, Direction direction, boolean upsert) { super(key); this.values = values; this.upsert = upsert; this.direction = direction; } /** * Creates a new {@link PushCommand} for right push ({@literal RPUSH}). * * @return a new {@link PushCommand} for right push ({@literal RPUSH}). */ public static PushCommand right() { return new PushCommand(null, null, Direction.RIGHT, true); } /** * Creates a new {@link PushCommand} for left push ({@literal LPUSH}). * * @return a new {@link PushCommand} for left push ({@literal LPUSH}). */ public static PushCommand left() { return new PushCommand(null, null, Direction.LEFT, true); } /** * Applies the {@literal value}. Constructs a new command instance with all previously configured properties. * * @param value must not be {@literal null}. * @return a new {@link PushCommand} with {@literal value} applied. */ public PushCommand value(ByteBuffer value) { Assert.notNull(value, "Value must not be null!"); return new PushCommand(null, Collections.singletonList(value), direction, upsert); } /** * Applies a {@link List} of {@literal values}. * * @param values must not be {@literal null}. * @return a new {@link PushCommand} with {@literal values} applied. */ public PushCommand values(List<ByteBuffer> values) { Assert.notNull(values, "Values must not be null!"); return new PushCommand(null, new ArrayList<>(values), direction, upsert); } /** * Applies the {@literal key}. Constructs a new command instance with all previously configured properties. * * @param key must not be {@literal null}. * @return a new {@link PushCommand} with {@literal key} applied. */ public PushCommand to(ByteBuffer key) { Assert.notNull(key, "Key must not be null!"); return new PushCommand(key, values, direction, upsert); } /** * Disable upsert. Constructs a new command instance with all previously configured properties. * * @return a new {@link PushCommand} with upsert disabled. */ public PushCommand ifExists() { return new PushCommand(getKey(), values, direction, false); } /** * @return */ public List<ByteBuffer> getValues() { return values; } /** * @return */ public boolean getUpsert() { return upsert; } /** * @return */ public Direction getDirection() { return direction; } } /** * Append {@literal values} to {@literal key}. * * @param key must not be {@literal null}. * @param values must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/rpush">Redis Documentation: RPUSH</a> */ default Mono<Long> rPush(ByteBuffer key, List<ByteBuffer> values) { Assert.notNull(key, "Key must not be null!"); Assert.notNull(values, "Values must not be null!"); return push(Mono.just(PushCommand.right().values(values).to(key))).next().map(NumericResponse::getOutput); } /** * Append {@literal values} to {@literal key} only if {@literal key} already exists. * * @param key must not be {@literal null}. * @param value must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/rpushx">Redis Documentation: RPUSHX</a> */ default Mono<Long> rPushX(ByteBuffer key, ByteBuffer value) { Assert.notNull(key, "Key must not be null!"); Assert.notNull(value, "Value must not be null!"); return push(Mono.just(PushCommand.right().value(value).to(key).ifExists())).next().map(NumericResponse::getOutput); } /** * Prepend {@literal values} to {@literal key}. * * @param key must not be {@literal null}. * @param values must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lpush">Redis Documentation: LPUSH</a> */ default Mono<Long> lPush(ByteBuffer key, List<ByteBuffer> values) { Assert.notNull(key, "Key must not be null!"); Assert.notNull(values, "Values must not be null!"); return push(Mono.just(PushCommand.left().values(values).to(key))).next().map(NumericResponse::getOutput); } /** * Prepend {@literal value} to {@literal key} if {@literal key} already exists. * * @param key must not be {@literal null}. * @param value must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lpushx">Redis Documentation: LPUSHX</a> */ default Mono<Long> lPushX(ByteBuffer key, ByteBuffer value) { Assert.notNull(key, "Key must not be null!"); Assert.notNull(value, "Value must not be null!"); return push(Mono.just(PushCommand.left().value(value).to(key).ifExists())).next().map(NumericResponse::getOutput); } /** * Prepend {@link PushCommand#getValues()} to {@link PushCommand#getKey()}. * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lpush">Redis Documentation: LPUSH</a> * @see <a href="http://redis.io/commands/rpush">Redis Documentation: RPUSH</a> */ Flux<NumericResponse<PushCommand, Long>> push(Publisher<PushCommand> commands); /** * Get the size of list stored at {@literal key}. * * @param key must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/llen">Redis Documentation: LLEN</a> */ default Mono<Long> lLen(ByteBuffer key) { Assert.notNull(key, "Key must not be null!"); return lLen(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput); } /** * Get the size of list stored at {@link KeyCommand#getKey()} * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/llen">Redis Documentation: LLEN</a> */ Flux<NumericResponse<KeyCommand, Long>> lLen(Publisher<KeyCommand> commands); /** * Get elements between {@literal begin} and {@literal end} from list at {@literal 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> */ default Flux<ByteBuffer> lRange(ByteBuffer key, long start, long end) { Assert.notNull(key, "Key must not be null!"); return lRange(Mono.just(RangeCommand.key(key).fromIndex(start).toIndex(end))).flatMap(CommandResponse::getOutput); } /** * Get elements in {@link RangeCommand#getRange()} from list at {@link RangeCommand#getKey()} * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lrange">Redis Documentation: LRANGE</a> */ Flux<CommandResponse<RangeCommand, Flux<ByteBuffer>>> lRange(Publisher<RangeCommand> commands); /** * Trim list at {@literal key} to elements between {@literal begin} and {@literal end}. * * @param key must not be {@literal null}. * @param start * @param end * @return * @see <a href="http://redis.io/commands/ltrim">Redis Documentation: LTRIM</a> */ default Mono<Boolean> lTrim(ByteBuffer key, long start, long end) { Assert.notNull(key, "Key must not be null!"); return lTrim(Mono.just(RangeCommand.key(key).fromIndex(start).toIndex(end))) // .next() // .map(BooleanResponse::getOutput); } /** * Trim list at {@link RangeCommand#getKey()} to elements within {@link RangeCommand#getRange()}. * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/ltrim">Redis Documentation: LTRIM</a> */ Flux<BooleanResponse<RangeCommand>> lTrim(Publisher<RangeCommand> commands); /** * {@code LINDEX} command parameters. * * @author Christoph Strobl * @see <a href="http://redis.io/commands/lindex">Redis Documentation: LINDEX</a> */ class LIndexCommand extends KeyCommand { private final Long index; private LIndexCommand(ByteBuffer key, Long index) { super(key); this.index = index; } /** * Creates a new {@link LIndexCommand} given an {@literal index}. * * @param index * @return a new {@link LIndexCommand} for {@literal index}. */ public static LIndexCommand elementAt(long index) { return new LIndexCommand(null, index); } /** * Applies the {@literal key}. Constructs a new command instance with all previously configured properties. * * @param key must not be {@literal null}. * @return a new {@link LIndexCommand} with {@literal key} applied. */ public LIndexCommand from(ByteBuffer key) { Assert.notNull(key, "Key must not be null!"); return new LIndexCommand(key, index); } /** * @return */ public Long getIndex() { return index; } } /** * Get element at {@literal index} form list at {@literal key}. * * @param key must not be {@literal null}. * @param index * @return * @see <a href="http://redis.io/commands/lindex">Redis Documentation: LINDEX</a> */ default Mono<ByteBuffer> lIndex(ByteBuffer key, long index) { Assert.notNull(key, "Key must not be null!"); return lIndex(Mono.just(LIndexCommand.elementAt(index).from(key))).next().map(ByteBufferResponse::getOutput); } /** * Get element at {@link LIndexCommand#getIndex()} form list at {@link LIndexCommand#getKey()}. * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lindex">Redis Documentation: LINDEX</a> */ Flux<ByteBufferResponse<LIndexCommand>> lIndex(Publisher<LIndexCommand> commands); /** * {@code LINSERT} command parameters. * * @author Christoph Strobl * @see <a href="http://redis.io/commands/linsert">Redis Documentation: LINSERT</a> */ class LInsertCommand extends KeyCommand { private final Position position; private final ByteBuffer pivot; private final ByteBuffer value; private LInsertCommand(ByteBuffer key, Position position, ByteBuffer pivot, ByteBuffer value) { super(key); this.position = position; this.pivot = pivot; this.value = value; } /** * Creates a new {@link LInsertCommand} given a {@link ByteBuffer value}. * * @param value must not be {@literal null}. * @return a new {@link LInsertCommand} for {@link ByteBuffer value}. */ public static LInsertCommand value(ByteBuffer value) { Assert.notNull(value, "Value must not be null!"); return new LInsertCommand(null, null, null, value); } /** * Applies the before {@literal pivot}. Constructs a new command instance with all previously configured properties. * * @param pivot must not be {@literal null}. * @return a new {@link LInsertCommand} with {@literal pivot} applied. */ public LInsertCommand before(ByteBuffer pivot) { Assert.notNull(pivot, "Before pivot must not be null!"); return new LInsertCommand(getKey(), Position.BEFORE, pivot, value); } /** * Applies the after {@literal pivot}. Constructs a new command instance with all previously configured properties. * * @param pivot must not be {@literal null}. * @return a new {@link LInsertCommand} with {@literal pivot} applied. */ public LInsertCommand after(ByteBuffer pivot) { Assert.notNull(pivot, "After pivot must not be null!"); return new LInsertCommand(getKey(), Position.AFTER, pivot, value); } /** * Applies the {@literal key}. Constructs a new command instance with all previously configured properties. * * @param key must not be {@literal null}. * @return a new {@link LInsertCommand} with {@literal key} applied. */ public LInsertCommand forKey(ByteBuffer key) { Assert.notNull(key, "Key must not be null!"); return new LInsertCommand(key, position, pivot, value); } /** * @return */ public ByteBuffer getValue() { return value; } /** * @return */ public Position getPosition() { return position; } /** * @return */ public ByteBuffer getPivot() { return pivot; } } /** * Insert {@literal value} {@link Position#BEFORE} or {@link Position#AFTER} existing {@literal pivot} for * {@literal key}. * * @param key must not be {@literal null}. * @param position must not be {@literal null}. * @param pivot must not be {@literal null}. * @param value must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/linsert">Redis Documentation: LINSERT</a> */ default Mono<Long> lInsert(ByteBuffer key, Position position, ByteBuffer pivot, ByteBuffer value) { Assert.notNull(key, "Key must not be null!"); Assert.notNull(position, "Position must not be null!"); Assert.notNull(pivot, "Pivot must not be null!"); Assert.notNull(value, "Value must not be null!"); LInsertCommand command = LInsertCommand.value(value); command = Position.BEFORE.equals(position) ? command.before(pivot) : command.after(pivot); command = command.forKey(key); return lInsert(Mono.just(command)).next().map(NumericResponse::getOutput); } /** * Insert {@link LInsertCommand#getValue()} {@link Position#BEFORE} or {@link Position#AFTER} existing * {@link LInsertCommand#getPivot()} for {@link LInsertCommand#getKey()} * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/linsert">Redis Documentation: LINSERT</a> */ Flux<NumericResponse<LInsertCommand, Long>> lInsert(Publisher<LInsertCommand> commands); /** * {@code LSET} command parameters. * * @author Christoph Strobl * @see <a href="http://redis.io/commands/lset">Redis Documentation: LSET</a> */ class LSetCommand extends KeyCommand { private final Long index; private final ByteBuffer value; private LSetCommand(ByteBuffer key, Long index, ByteBuffer value) { super(key); this.index = index; this.value = value; } /** * Creates a new {@link LSetCommand} given an {@literal index}. * * @param index * @return a new {@link LSetCommand} for {@literal index}. */ public static LSetCommand elementAt(long index) { return new LSetCommand(null, index, null); } /** * Applies the {@literal value}. Constructs a new command instance with all previously configured properties. * * @param value must not be {@literal null}. * @return a new {@link LSetCommand} with {@literal value} applied. */ public LSetCommand to(ByteBuffer value) { Assert.notNull(value, "Value must not be null!"); return new LSetCommand(getKey(), index, value); } /** * Applies the {@literal key}. Constructs a new command instance with all previously configured properties. * * @param key must not be {@literal null}. * @return a new {@link LSetCommand} with {@literal value} applied. */ public LSetCommand forKey(ByteBuffer key) { Assert.notNull(key, "Key must not be null!"); return new LSetCommand(key, index, value); } /** * @return */ public ByteBuffer getValue() { return value; } /** * @return */ public Long getIndex() { return index; } } /** * Set the {@literal value} list element at {@literal index}. * * @param key must not be {@literal null}. * @param index * @param value must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lset">Redis Documentation: LSET</a> */ default Mono<Boolean> lSet(ByteBuffer key, long index, ByteBuffer value) { Assert.notNull(key, "Key must not be null!"); Assert.notNull(value, "Value must not be null!"); return lSet(Mono.just(LSetCommand.elementAt(index).to(value).forKey(key))).next().map(BooleanResponse::getOutput); } /** * Set the {@link LSetCommand#getValue()} list element at {@link LSetCommand#getKey()}. * * @param commands * @return * @see <a href="http://redis.io/commands/lset">Redis Documentation: LSET</a> */ Flux<BooleanResponse<LSetCommand>> lSet(Publisher<LSetCommand> commands); /** * {@code LREM} command parameters. * * @author Christoph Strobl * @see <a href="http://redis.io/commands/lrem">Redis Documentation: LREM</a> */ class LRemCommand extends KeyCommand { private final Long count; private final ByteBuffer value; private LRemCommand(ByteBuffer key, Long count, ByteBuffer value) { super(key); this.count = count; this.value = value; } /** * Creates a new {@link LRemCommand} to delete all values. * * @return a new {@link LRemCommand} for {@link ByteBuffer value}. */ public static LRemCommand all() { return new LRemCommand(null, 0L, null); } /** * Creates a new {@link LRemCommand} to first {@literal count} values. * * @return a new {@link LRemCommand} to delete first {@literal count} values. */ public static LRemCommand first(long count) { return new LRemCommand(null, count, null); } /** * Creates a new {@link LRemCommand} to last {@literal count} values. * * @return a new {@link LRemCommand} to delete last {@literal count} values. */ public static LRemCommand last(long count) { Long value = count < 0L ? count : Math.negateExact(count); return new LRemCommand(null, value, null); } /** * Applies the {@literal value}. Constructs a new command instance with all previously configured properties. * * @param value must not be {@literal null}. * @return a new {@link LRemCommand} with {@literal value} applied. */ public LRemCommand occurrencesOf(ByteBuffer value) { Assert.notNull(value, "Value must not be null!"); return new LRemCommand(getKey(), count, value); } /** * Applies the {@literal key}. Constructs a new command instance with all previously configured properties. * * @param key must not be {@literal null}. * @return a new {@link LRemCommand} with {@literal key} applied. */ public LRemCommand from(ByteBuffer key) { Assert.notNull(key, "Key must not be null!"); return new LRemCommand(key, count, value); } /** * @return */ public Long getCount() { return count; } /** * @return */ public ByteBuffer getValue() { return value; } } /** * Removes all occurrences of {@literal value} from the list stored at {@literal key}. * * @param key must not be {@literal null}. * @param value must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lrem">Redis Documentation: LREM</a> */ default Mono<Long> lRem(ByteBuffer key, ByteBuffer value) { Assert.notNull(key, "Key must not be null!"); Assert.notNull(value, "Value must not be null!"); return lRem(Mono.just(LRemCommand.all().occurrencesOf(value).from(key))).next().map(NumericResponse::getOutput); } /** * Removes the first {@literal count} occurrences of {@literal value} from the list stored at {@literal key}. * * @param key must not be {@literal null}. * @param count must not be {@literal null}. * @param value must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lrem">Redis Documentation: LREM</a> */ default Mono<Long> lRem(ByteBuffer key, Long count, ByteBuffer value) { Assert.notNull(key, "Key must not be null!"); Assert.notNull(count, "Count must not be null!"); Assert.notNull(value, "Value must not be null!"); return lRem(Mono.just(LRemCommand.first(count).occurrencesOf(value).from(key))).next() .map(NumericResponse::getOutput); } /** * Removes the {@link LRemCommand#getCount()} occurrences of {@link LRemCommand#getValue()} from the list stored at * {@link LRemCommand#getKey()}. * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lrem">Redis Documentation: LREM</a> */ Flux<NumericResponse<LRemCommand, Long>> lRem(Publisher<LRemCommand> commands); /** * {@code LPOP}/{@literal RPOP} command parameters. * * @author Christoph Strobl * @see <a href="http://redis.io/commands/lpop">Redis Documentation: LPOP</a> * @see <a href="http://redis.io/commands/rpop">Redis Documentation: RPOP</a> */ class PopCommand extends KeyCommand { private final Direction direction; private PopCommand(ByteBuffer key, Direction direction) { super(key); this.direction = direction; } /** * Creates a new {@link PopCommand} for right push ({@literal RPOP}). * * @return a new {@link PopCommand} for right push ({@literal RPOP}). */ public static PopCommand right() { return new PopCommand(null, Direction.RIGHT); } /** * Creates a new {@link PopCommand} for right push ({@literal LPOP}). * * @return a new {@link PopCommand} for right push ({@literal LPOP}). */ public static PopCommand left() { return new PopCommand(null, Direction.LEFT); } /** * Applies the {@literal key}. Constructs a new command instance with all previously configured properties. * * @param key must not be {@literal null}. * @return a new {@link LSetCommand} with {@literal value} applied. */ public PopCommand from(ByteBuffer key) { Assert.notNull(key, "Key must not be null!"); return new PopCommand(key, direction); } /** * @return */ public Direction getDirection() { return direction; } } /** * Removes and returns first element in list stored at {@literal key}. * * @param key must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lpop">Redis Documentation: LPOP</a> */ default Mono<ByteBuffer> lPop(ByteBuffer key) { Assert.notNull(key, "Key must not be null!"); return pop(Mono.just(PopCommand.left().from(key))).next().map(ByteBufferResponse::getOutput); } /** * Removes and returns last element in list stored at {@literal key}. * * @param key must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/rpop">Redis Documentation: RPOP</a> */ default Mono<ByteBuffer> rPop(ByteBuffer key) { Assert.notNull(key, "Key must not be null!"); return pop(Mono.just(PopCommand.right().from(key))).next().map(ByteBufferResponse::getOutput); } /** * Removes and returns last element in list stored at {@link KeyCommand#getKey()} * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/lpop">Redis Documentation: LPOP</a> * @see <a href="http://redis.io/commands/rpop">Redis Documentation: RPOP</a> */ Flux<ByteBufferResponse<PopCommand>> pop(Publisher<PopCommand> commands); /** * @author Christoph Strobl * @see <a href="http://redis.io/commands/blpop">Redis Documentation: BLPOP</a> * @see <a href="http://redis.io/commands/brpop">Redis Documentation: BRPOP</a> */ class BPopCommand implements Command { private final List<ByteBuffer> keys; private final Duration timeout; private final Direction direction; private BPopCommand(List<ByteBuffer> keys, Duration timeout, Direction direction) { this.keys = keys; this.timeout = timeout; this.direction = direction; } /** * Creates a new {@link BPopCommand} for right push ({@literal BRPOP}). * * @return a new {@link BPopCommand} for right push ({@literal BRPOP}). */ public static BPopCommand right() { return new BPopCommand(null, Duration.ZERO, Direction.RIGHT); } /** * Creates a new {@link BPopCommand} for right push ({@literal BLPOP}). * * @return a new {@link BPopCommand} for right push ({@literal BLPOP}). */ public static BPopCommand left() { return new BPopCommand(null, Duration.ZERO, Direction.LEFT); } /** * Applies the {@literal value}. Constructs a new command instance with all previously configured properties. * * @param keys must not be {@literal null}. * @return a new {@link BPopCommand} with {@literal value} applied. */ public BPopCommand from(List<ByteBuffer> keys) { Assert.notNull(keys, "Keys must not be null!"); return new BPopCommand(new ArrayList<>(keys), Duration.ZERO, direction); } /** * Applies a {@link Duration timeout}. Constructs a new command instance with all previously configured properties. * * @param timeout must not be {@literal null}. * @return a new {@link BPopCommand} with {@link Duration timeout} applied. */ public BPopCommand blockingFor(Duration timeout) { Assert.notNull(timeout, "Timeout must not be null!"); return new BPopCommand(keys, timeout, direction); } /* (non-Javadoc) * @see org.springframework.data.redis.connection.ReactiveRedisConnection.Command#getKey() */ @Override public ByteBuffer getKey() { return null; } /** * @return */ public List<ByteBuffer> getKeys() { return keys; } /** * @return */ public Duration getTimeout() { return timeout; } /** * @return */ public Direction getDirection() { return direction; } } /** * Result for {@link PopCommand}/{@link BPopCommand}. * * @author Christoph Strobl */ class PopResult { private final List<ByteBuffer> result; public PopResult(List<ByteBuffer> result) { this.result = result; } public ByteBuffer getKey() { return ObjectUtils.isEmpty(result) ? null : result.get(0); } public ByteBuffer getValue() { return ObjectUtils.isEmpty(result) ? null : result.get(1); } public List<ByteBuffer> getRaw() { return Collections.unmodifiableList(result); } } /** * Result for {@link PopCommand}/{@link BPopCommand}. * * @author Christoph Strobl */ class PopResponse extends CommandResponse<BPopCommand, PopResult> { public PopResponse(BPopCommand input, PopResult output) { super(input, output); } } /** * Removes and returns first element from lists stored at {@literal keys}. <br> * <b>Blocks connection</b> until element available or {@literal timeout} reached. * * @param keys must not be {@literal null}. * @param timeout must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/blpop">Redis Documentation: BLPOP</a> */ default Mono<PopResult> blPop(List<ByteBuffer> keys, Duration timeout) { Assert.notNull(keys, "Keys must not be null."); Assert.notNull(timeout, "Timeout must not be null."); return bPop(Mono.just(BPopCommand.left().from(keys).blockingFor(timeout))).next().map(PopResponse::getOutput); } /** * Removes and returns last element from lists stored at {@literal keys}. <br> * <b>Blocks connection</b> until element available or {@literal timeout} reached. * * @param keys must not be {@literal null}. * @param timeout must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/brpop">Redis Documentation: BRPOP</a> */ default Mono<PopResult> brPop(List<ByteBuffer> keys, Duration timeout) { Assert.notNull(keys, "Keys must not be null."); Assert.notNull(timeout, "Timeout must not be null."); return bPop(Mono.just(BPopCommand.right().from(keys).blockingFor(timeout))).next().map(PopResponse::getOutput); } /** * Removes and returns the top {@link BPopCommand#getDirection()} element from lists stored at * {@link BPopCommand#getKeys()}.<br> * <b>Blocks connection</b> until element available or {@link BPopCommand#getTimeout()} reached. * * @param commands * @return * @see <a href="http://redis.io/commands/blpop">Redis Documentation: BLPOP</a> * @see <a href="http://redis.io/commands/brpop">Redis Documentation: BRPOP</a> */ Flux<PopResponse> bPop(Publisher<BPopCommand> commands); /** * {@code RPOPLPUSH} command parameters. * * @author Christoph Strobl * @see <a href="http://redis.io/commands/rpoplpush">Redis Documentation: RPOPLPUSH</a> */ class RPopLPushCommand extends KeyCommand { private final ByteBuffer destination; private RPopLPushCommand(ByteBuffer key, ByteBuffer destination) { super(key); this.destination = destination; } /** * Creates a new {@link RPopLPushCommand} given a {@literal sourceKey}. * * @param sourceKey must not be {@literal null}. * @return a new {@link RPopLPushCommand} for a {@literal sourceKey}. */ public static RPopLPushCommand from(ByteBuffer sourceKey) { Assert.notNull(sourceKey, "Source key must not be null!"); return new RPopLPushCommand(sourceKey, null); } /** * Applies the {@literal destinationKey}. Constructs a new command instance with all previously configured * properties. * * @param destinationKey must not be {@literal null}. * @return a new {@link BPopCommand} with {@literal value} applied. */ public RPopLPushCommand to(ByteBuffer destinationKey) { Assert.notNull(destinationKey, "Destination key must not be null!"); return new RPopLPushCommand(getKey(), destinationKey); } /** * @return */ public ByteBuffer getDestination() { return destination; } } /** * Remove the last element from list at {@literal source}, append it to {@literal destination} and return its value. * * @param source must not be {@literal null}. * @param destination must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/rpoplpush">Redis Documentation: RPOPLPUSH</a> */ default Mono<ByteBuffer> rPopLPush(ByteBuffer source, ByteBuffer destination) { Assert.notNull(source, "Source must not be null!"); Assert.notNull(destination, "Destination must not be null!"); return rPopLPush(Mono.just(RPopLPushCommand.from(source).to(destination))) // .next() // .map(ByteBufferResponse::getOutput); } /** * Remove the last element from list at {@link RPopLPushCommand#getKey()}, append it to * {@link RPopLPushCommand#getDestination()} and return its value. * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/rpoplpush">Redis Documentation: RPOPLPUSH</a> */ Flux<ByteBufferResponse<RPopLPushCommand>> rPopLPush(Publisher<RPopLPushCommand> commands); /** * {@code BRPOPLPUSH} command parameters. * * @author Christoph Strobl * @see <a href="http://redis.io/commands/brpoplpush">Redis Documentation: BRPOPLPUSH</a> */ class BRPopLPushCommand extends KeyCommand { private final ByteBuffer destination; private final Duration timeout; private BRPopLPushCommand(ByteBuffer key, ByteBuffer destination, Duration timeout) { super(key); this.destination = destination; this.timeout = timeout; } /** * Creates a new {@link BRPopLPushCommand} given a {@literal sourceKey}. * * @param sourceKey must not be {@literal null}. * @return a new {@link BRPopLPushCommand} for a {@literal sourceKey}. */ public static BRPopLPushCommand from(ByteBuffer sourceKey) { Assert.notNull(sourceKey, "Source key must not be null!"); return new BRPopLPushCommand(sourceKey, null, null); } /** * Applies the {@literal destinationKey}. Constructs a new command instance with all previously configured * properties. * * @param destinationKey must not be {@literal null}. * @return a new {@link BRPopLPushCommand} with {@literal value} applied. */ public BRPopLPushCommand to(ByteBuffer destinationKey) { Assert.notNull(destinationKey, "Destination key must not be null!"); return new BRPopLPushCommand(getKey(), destinationKey, timeout); } /** * Applies a {@link Duration timeout}. Constructs a new command instance with all previously configured properties. * * @param timeout must not be {@literal null}. * @return a new {@link BRPopLPushCommand} with {@link Duration timeout} applied. */ public BRPopLPushCommand blockingFor(Duration timeout) { Assert.notNull(timeout, "Timeout must not be null!"); return new BRPopLPushCommand(getKey(), destination, timeout); } /** * @return */ public ByteBuffer getDestination() { return destination; } /** * @return */ public Duration getTimeout() { return timeout; } } /** * Remove the last element from list at {@literal source}, append it to {@literal destination} and return its value. * <b>Blocks connection</b> until element available or {@literal timeout} reached. <br /> * * @param source must not be {@literal null}. * @param destination must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/brpoplpush">Redis Documentation: BRPOPLPUSH</a> */ default Mono<ByteBuffer> bRPopLPush(ByteBuffer source, ByteBuffer destination, Duration timeout) { Assert.notNull(source, "Source must not be null!"); Assert.notNull(destination, "Destination must not be null!"); return bRPopLPush(Mono.just(BRPopLPushCommand.from(source).to(destination).blockingFor(timeout))).next() .map(ByteBufferResponse::getOutput); } /** * Remove the last element from list at {@link BRPopLPushCommand#getKey()}, append it to * {@link BRPopLPushCommand#getDestination()} and return its value. <br /> * <b>Blocks connection</b> until element available or {@link BRPopLPushCommand#getTimeout()} reached. * * @param commands must not be {@literal null}. * @return * @see <a href="http://redis.io/commands/brpoplpush">Redis Documentation: BRPOPLPUSH</a> */ Flux<ByteBufferResponse<BRPopLPushCommand>> bRPopLPush(Publisher<BRPopLPushCommand> commands); }