/*
* 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.lettuce;
import io.lettuce.core.AbstractRedisClient;
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulConnection;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands;
import io.lettuce.core.codec.RedisCodec;
import reactor.core.publisher.Flux;
import java.nio.ByteBuffer;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.data.redis.connection.ReactiveGeoCommands;
import org.springframework.data.redis.connection.ReactiveHashCommands;
import org.springframework.data.redis.connection.ReactiveHyperLogLogCommands;
import org.springframework.data.redis.connection.ReactiveKeyCommands;
import org.springframework.data.redis.connection.ReactiveListCommands;
import org.springframework.data.redis.connection.ReactiveNumberCommands;
import org.springframework.data.redis.connection.ReactiveRedisConnection;
import org.springframework.data.redis.connection.ReactiveSetCommands;
import org.springframework.data.redis.connection.ReactiveStringCommands;
import org.springframework.data.redis.connection.ReactiveZSetCommands;
import org.springframework.util.Assert;
/**
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.0
*/
class LettuceReactiveRedisConnection implements ReactiveRedisConnection {
private StatefulConnection<ByteBuffer, ByteBuffer> connection;
private static final RedisCodec<ByteBuffer, ByteBuffer> CODEC = ByteBufferCodec.INSTANCE;
public LettuceReactiveRedisConnection(AbstractRedisClient client) {
Assert.notNull(client, "RedisClient must not be null!");
if (client instanceof RedisClient) {
connection = ((RedisClient) client).connect(CODEC);
} else if (client instanceof RedisClusterClient) {
connection = ((RedisClusterClient) client).connect(CODEC);
} else {
throw new InvalidDataAccessResourceUsageException(
String.format("Cannot use client of type %s", client.getClass()));
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#keyCommands()
*/
@Override
public ReactiveKeyCommands keyCommands() {
return new LettuceReactiveKeyCommands(this);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#stringCommands()
*/
@Override
public ReactiveStringCommands stringCommands() {
return new LettuceReactiveStringCommands(this);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#numberCommands()
*/
@Override
public ReactiveNumberCommands numberCommands() {
return new LettuceReactiveNumberCommands(this);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#listCommands()
*/
@Override
public ReactiveListCommands listCommands() {
return new LettuceReactiveListCommands(this);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#setCommands()
*/
@Override
public ReactiveSetCommands setCommands() {
return new LettuceReactiveSetCommands(this);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#zSetCommands()
*/
@Override
public ReactiveZSetCommands zSetCommands() {
return new LettuceReactiveZSetCommands(this);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#hashCommands()
*/
@Override
public ReactiveHashCommands hashCommands() {
return new LettuceReactiveHashCommands(this);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#geoCommands()
*/
@Override
public ReactiveGeoCommands geoCommands() {
return new LettuceReactiveGeoCommands(this);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#hyperLogLogCommands()
*/
@Override
public ReactiveHyperLogLogCommands hyperLogLogCommands() {
return new LettuceReactiveHyperLogLogCommands(this);
}
/**
* @param callback
* @return
*/
public <T> Flux<T> execute(LettuceReactiveCallback<T> callback) {
return Flux.defer(() -> callback.doWithCommands(getCommands())).onErrorMap(translateException());
}
/* (non-Javadoc)
* @see java.io.Closeable#close()
*/
@Override
public void close() {
connection.close();
}
protected StatefulConnection<ByteBuffer, ByteBuffer> getConnection() {
return connection;
}
protected RedisClusterReactiveCommands<ByteBuffer, ByteBuffer> getCommands() {
if (connection instanceof StatefulRedisConnection) {
return ((StatefulRedisConnection<ByteBuffer, ByteBuffer>) connection).reactive();
} else if (connection instanceof StatefulRedisClusterConnection) {
return ((StatefulRedisClusterConnection<ByteBuffer, ByteBuffer>) connection).reactive();
}
throw new RuntimeException("o.O unknown connection type " + connection);
}
<T> Function<Throwable, Throwable> translateException() {
return throwable -> {
if (throwable instanceof RuntimeException) {
DataAccessException convertedException = LettuceConverters.exceptionConverter()
.convert((RuntimeException) throwable);
return convertedException != null ? convertedException : throwable;
}
return throwable;
};
}
interface LettuceReactiveCallback<T> {
Publisher<T> doWithCommands(RedisClusterReactiveCommands<ByteBuffer, ByteBuffer> cmd);
}
static enum ByteBufferCodec implements RedisCodec<ByteBuffer, ByteBuffer> {
INSTANCE;
@Override
public ByteBuffer decodeKey(ByteBuffer bytes) {
ByteBuffer buffer = ByteBuffer.allocate(bytes.remaining());
buffer.put(bytes);
buffer.flip();
return buffer;
}
@Override
public ByteBuffer decodeValue(ByteBuffer bytes) {
return decodeKey(bytes);
}
@Override
public ByteBuffer encodeKey(ByteBuffer key) {
return key.duplicate();
}
@Override
public ByteBuffer encodeValue(ByteBuffer value) {
return value.duplicate();
}
}
}