/*
* 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 lombok.Data;
import java.io.Closeable;
import java.nio.ByteBuffer;
import java.util.List;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Range.Bound;
import org.springframework.util.Assert;
/**
* Redis connection using reactive infrastructure declaring entry points for reactive command execution.
* <p>
* {@link ReactiveRedisConnection} is typically implemented by a stateful object that requires to be {@link #close()
* closed} once it is no longer required.
* <p>
* Commands can be either executed by passing plain arguments like {@code key}, {@code value} or wrapped inside a
* command stream. Streaming command execution accepts {@link org.reactivestreams.Publisher} of a particular
* {@link Command}. Commands are executed at the time their emission.
* <p>
* Arguments are binary-safe by using {@link ByteBuffer} arguments. Expect {@link ByteBuffer} to be consumed by
* {@link ReactiveRedisConnection} invocation or during execution. Any {@link ByteBuffer} used as method parameter
* should not be altered after invocation.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.0
* @see Command
* @see CommandResponse
* @see KeyCommand
*/
public interface ReactiveRedisConnection extends Closeable {
@Override
void close();
/**
* Get {@link ReactiveKeyCommands}.
*
* @return never {@literal null}.
*/
ReactiveKeyCommands keyCommands();
/**
* Get {@link ReactiveStringCommands}.
*
* @return never {@literal null}.
*/
ReactiveStringCommands stringCommands();
/**
* Get {@link ReactiveNumberCommands}
*
* @return never {@literal null}.
*/
ReactiveNumberCommands numberCommands();
/**
* Get {@link ReactiveListCommands}.
*
* @return never {@literal null}.
*/
ReactiveListCommands listCommands();
/**
* Get {@link ReactiveSetCommands}.
*
* @return never {@literal null}.
*/
ReactiveSetCommands setCommands();
/**
* Get {@link ReactiveZSetCommands}.
*
* @return never {@literal null}.
*/
ReactiveZSetCommands zSetCommands();
/**
* Get {@link ReactiveHashCommands}.
*
* @return
*/
ReactiveHashCommands hashCommands();
/**
* Get {@link ReactiveGeoCommands}
*
* @return never {@literal null}.
*/
ReactiveGeoCommands geoCommands();
/**
* Get {@link ReactiveHyperLogLogCommands}.
*
* @return never {@literal null}.
*/
ReactiveHyperLogLogCommands hyperLogLogCommands();
/**
* Base interface for Redis commands executed with a reactive infrastructure.
*
* @author Christoph Strobl
* @author Mark Paluch
*/
interface Command {
/**
* @return the key related to this command.
*/
ByteBuffer getKey();
/**
* @return command name as {@link String}.
*/
default String getName() {
return getClass().getSimpleName().replace("Command", "").toUpperCase();
}
}
/**
* {@link Command} for key-bound operations.
*
* @author Christoph Strobl
*/
class KeyCommand implements Command {
private ByteBuffer key;
/**
* Creates a new {@link KeyCommand} given a {@code key}.
*
* @param key must not be {@literal null}.
*/
public KeyCommand(ByteBuffer key) {
this.key = key;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.Command#getKey()
*/
@Override
public ByteBuffer getKey() {
return key;
}
}
/**
* @author Christoph Strobl
*/
class RangeCommand extends KeyCommand {
Range<Long> range;
/**
* Creates a new {@link RangeCommand} given a {@code key} and {@link Range}.
*
* @param key must not be {@literal null}.
* @param range may be {@literal null} if unbounded.
*/
private RangeCommand(ByteBuffer key, Range<Long> range) {
super(key);
this.range = range != null ? range : new Range<>(0L, Long.MAX_VALUE);
}
/**
* Creates a new {@link RangeCommand} given a {@code key}.
*
* @param key must not be {@literal null}.
* @return a new {@link RangeCommand} for {@code key}.
*/
public static RangeCommand key(ByteBuffer key) {
return new RangeCommand(key, null);
}
/**
* Applies a {@link Range}. Constructs a new command instance with all previously configured properties.
*
* @param range must not be {@literal null}.
* @return a new {@link RangeCommand} with {@link Range} applied.
*/
public RangeCommand within(Range<Long> range) {
Assert.notNull(range, "Range must not be null!");
return new RangeCommand(getKey(), range);
}
/**
* Applies a lower bound to the {@link Range}. Constructs a new command instance with all previously configured
* properties.
*
* @param start
* @return a new {@link RangeCommand} with the lower bound applied.
*/
public RangeCommand fromIndex(long start) {
return new RangeCommand(getKey(), Range.of(Bound.inclusive(start), range.getUpperBound()));
}
/**
* Applies an upper bound to the {@link Range}. Constructs a new command instance with all previously configured
* properties.
*
* @param end
* @return a new {@link RangeCommand} with the upper bound applied.
*/
public RangeCommand toIndex(long end) {
return new RangeCommand(getKey(), Range.of(range.getLowerBound(), Bound.inclusive(end)));
}
/**
* @return the {@link Range}.
*/
public Range<Long> getRange() {
return range;
}
}
/**
* Base class for command responses.
*
* @param <I> command input type.
* @param <O> command output type.
*/
@Data
class CommandResponse<I, O> {
private final I input;
private final O output;
}
/**
* {@link CommandResponse} implementation for {@link Boolean} responses.
*/
class BooleanResponse<I> extends CommandResponse<I, Boolean> {
public BooleanResponse(I input, Boolean output) {
super(input, output);
}
}
/**
* {@link CommandResponse} implementation for {@link ByteBuffer} responses.
*/
class ByteBufferResponse<I> extends CommandResponse<I, ByteBuffer> {
public ByteBufferResponse(I input, ByteBuffer output) {
super(input, output);
}
}
/**
* {@link CommandResponse} implementation for {@link List} responses.
*/
class MultiValueResponse<I, O> extends CommandResponse<I, List<O>> {
public MultiValueResponse(I input, List<O> output) {
super(input, output);
}
}
/**
* {@link CommandResponse} implementation for {@link Number numeric} responses.
*/
class NumericResponse<I, O extends Number> extends CommandResponse<I, O> {
public NumericResponse(I input, O output) {
super(input, output);
}
}
}