add support for Lettuce Redis driver

DATAREDIS-113
This commit is contained in:
Costin Leau
2013-02-07 17:21:13 +02:00
parent 0b70fa2d23
commit e472a5f5e5
9 changed files with 2313 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2011-2013 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 java.nio.ByteBuffer;
import com.lambdaworks.redis.codec.RedisCodec;
/**
* Basic codec that returns the raw data as byte[].
*
* @author Costin Leau
*/
class BytesRedisCodec extends RedisCodec<byte[], byte[]> {
@Override
public byte[] decodeKey(ByteBuffer bytes) {
return getBytes(bytes);
}
@Override
public byte[] decodeValue(ByteBuffer bytes) {
return getBytes(bytes);
}
@Override
public byte[] encodeKey(byte[] key) {
return key;
}
@Override
public byte[] encodeValue(byte[] value) {
return value;
}
private static byte[] getBytes(ByteBuffer buffer) {
byte[] b = new byte[buffer.remaining()];
buffer.get(b);
return b;
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2011-2013 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 java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import com.lambdaworks.redis.RedisClient;
/**
* Connection factory creating <a href="http://github.com/wg/lettuce">Lettuce</a>-based connections.
*
* @author Costin Leau
*/
/**
* @author Costin Leau
*/
public class LettuceConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
private String hostName = "localhost";
private int port = 6379;
private RedisClient client;
private long timeout = TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS);
/**
* Constructs a new <code>LettuceConnectionFactory</code> instance
* with default settings.
*/
public LettuceConnectionFactory() {
}
/**
* Constructs a new <code>LettuceConnectionFactory</code> instance
* with default settings.
*/
public LettuceConnectionFactory(String host, int port) {
this.hostName = host;
this.port = port;
}
public void afterPropertiesSet() {
client = new RedisClient(hostName, port);
client.setDefaultTimeout(timeout, TimeUnit.MILLISECONDS);
}
public void destroy() {
client.shutdown();
}
public RedisConnection getConnection() {
return new LettuceConnection(client.connectAsync(LettuceUtils.CODEC), timeout, client);
}
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return LettuceUtils.convertRedisAccessException(ex);
}
/**
* Returns the current host.
*
* @return the host
*/
public String getHostName() {
return hostName;
}
/**
* Sets the host.
*
* @param host the host to set
*/
public void setHostName(String host) {
this.hostName = host;
}
/**
* Returns the current port.
*
* @return the port
*/
public int getPort() {
return port;
}
/**
* Sets the port.
*
* @param port the port to set
*/
public void setPort(int port) {
this.port = port;
}
/**
* Returns the connection timeout (in milliseconds).
*
* @return connection timeout
*/
public long getTimeout() {
return timeout;
}
/**
* Sets the connection timeout (in milliseconds).
*
* @param timeout connection timeout
*/
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2011-2013 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 org.springframework.data.redis.connection.DefaultMessage;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.util.Assert;
import com.lambdaworks.redis.pubsub.RedisPubSubListener;
/**
* MessageListener wrapper around Lettuce {@link RedisPubSubListener}.
*
* @author Costin Leau
*/
class LettuceMessageListener implements RedisPubSubListener<byte[], byte[]> {
private final MessageListener listener;
LettuceMessageListener(MessageListener listener) {
Assert.notNull(listener, "message listener is required");
this.listener = listener;
}
public void message(byte[] channel, byte[] message) {
listener.onMessage(new DefaultMessage(channel, message), null);
}
public void message(byte[] pattern, byte[] channel, byte[] message) {
listener.onMessage(new DefaultMessage(channel, message), pattern);
}
public void subscribed(byte[] channel, long count) {
}
public void psubscribed(byte[] pattern, long count) {
}
public void unsubscribed(byte[] channel, long count) {
}
public void punsubscribed(byte[] pattern, long count) {
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2011-2013 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 org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.util.AbstractSubscription;
import com.lambdaworks.redis.pubsub.RedisPubSubConnection;
/**
* Message subscription on top of Lettuce.
*
* @author Costin Leau
*/
class LettuceSubscription extends AbstractSubscription {
private final RedisPubSubConnection<byte[], byte[]> pubsub;
private LettuceMessageListener listener;
LettuceSubscription(MessageListener listener, RedisPubSubConnection<byte[], byte[]> pubsubConnection) {
super(listener);
this.pubsub = pubsubConnection;
this.listener = new LettuceMessageListener(listener);
pubsub.addListener(this.listener);
}
protected void doClose() {
pubsub.unsubscribe(new byte[0]);
pubsub.punsubscribe(new byte[0]);
pubsub.removeListener(this.listener);
}
protected void doPsubscribe(byte[]... patterns) {
pubsub.psubscribe(patterns);
}
protected void doPUnsubscribe(boolean all, byte[]... patterns) {
if (all) {
pubsub.punsubscribe(new byte[0]);
}
else {
pubsub.punsubscribe(patterns);
}
}
protected void doSubscribe(byte[]... channels) {
pubsub.subscribe(channels);
}
protected void doUnsubscribe(boolean all, byte[]... channels) {
if (all) {
pubsub.unsubscribe(new byte[0]);
}
else {
pubsub.unsubscribe(channels);
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2011-2013 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 java.io.StringReader;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.connection.DefaultTuple;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
import org.springframework.data.redis.connection.SortParameters;
import org.springframework.data.redis.connection.SortParameters.Order;
import org.springframework.util.Assert;
import com.lambdaworks.redis.KeyValue;
import com.lambdaworks.redis.RedisCommandInterruptedException;
import com.lambdaworks.redis.RedisException;
import com.lambdaworks.redis.ScoredValue;
import com.lambdaworks.redis.SortArgs;
import com.lambdaworks.redis.ZStoreArgs;
import com.lambdaworks.redis.codec.RedisCodec;
import com.lambdaworks.redis.protocol.Charsets;
/**
* Helper class featuring methods for Lettuce connection handling, providing support for exception translation.
*
* @author Costin Leau
*/
abstract class LettuceUtils {
static final RedisCodec<byte[], byte[]> CODEC = new BytesRedisCodec();
static DataAccessException convertRedisAccessException(RuntimeException ex) {
if (ex instanceof RedisCommandInterruptedException) {
return new RedisSystemException("Redis command interrupted", ex);
}
if (ex instanceof RedisException) {
return new RedisSystemException("Redis exception", ex);
}
return null;
}
static Properties info(String reply) {
Properties info = new Properties();
StringReader stringReader = new StringReader(reply);
try {
info.load(stringReader);
} catch (Exception ex) {
throw new RedisSystemException("Cannot read Redis info", ex);
} finally {
stringReader.close();
}
return info;
}
static int asBit(boolean value) {
return (value ? 1 : 0);
}
static boolean convertPosition(Position where) {
Assert.notNull("list positions are mandatory");
return (Position.AFTER.equals(where) ? false : true);
}
static Set<Tuple> convertTuple(List<ScoredValue<byte[]>> zrange) {
Set<Tuple> tuples = new LinkedHashSet<Tuple>(zrange.size());
for (int i = 0; i < zrange.size(); i++) {
tuples.add(new DefaultTuple(zrange.get(i).value, Double.valueOf(zrange.get(i).score)));
}
return tuples;
}
static SortArgs sort(SortParameters params) {
SortArgs args = new SortArgs();
if (params.getByPattern() != null) {
args.by(new String(params.getByPattern(), Charsets.ASCII));
}
if (params.getLimit() != null) {
args.limit(params.getLimit().getStart(), params.getLimit().getCount());
}
if (params.getGetPattern() != null) {
byte[][] pattern = params.getGetPattern();
for (byte[] bs : pattern) {
args.get(new String(bs, Charsets.ASCII));
}
}
if (params.getOrder() != null) {
if (params.getOrder() == Order.ASC) {
args.asc();
}
else {
args.desc();
}
}
if (params.isAlphabetic()) {
args.alpha();
}
return args;
}
static ZStoreArgs zArgs(Aggregate aggregate, int[] weights) {
ZStoreArgs args = new ZStoreArgs();
if (aggregate != null) {
switch (aggregate) {
case MIN:
args.min();
break;
case MAX:
args.max();
break;
default:
args.sum();
break;
}
}
long[] lg = new long[weights.length];
for (int i = 0; i < lg.length; i++) {
lg[i] = (long) weights[i];
}
args.weights(lg);
return args;
}
static List<byte[]> toList(KeyValue<byte[], byte[]> blpop) {
List<byte[]> list = new ArrayList<byte[]>(2);
list.add(blpop.key);
list.add(blpop.value);
return list;
}
}

View File

@@ -0,0 +1,5 @@
/**
* Connection package for <a href="https://github.com/wg/lettuce">Lettuce</a> Redis client.
*/
package org.springframework.data.redis.connection.lettuce;