lettuce sources removed. #183
parent
16e770cd46
commit
18febb690e
@ -1,62 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.codec;
|
||||
|
||||
/**
|
||||
* High-performance base16 (AKA hex) codec.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class Base16 {
|
||||
private static final char[] upper = "0123456789ABCDEF".toCharArray();
|
||||
private static final char[] lower = "0123456789abcdef".toCharArray();
|
||||
private static final byte[] decode = new byte[128];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
decode['0' + i] = (byte) i;
|
||||
decode['A' + i] = (byte) (10 + i);
|
||||
decode['a' + i] = (byte) (10 + i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode bytes to base16 chars.
|
||||
*
|
||||
* @param src Bytes to encode.
|
||||
* @param upper Use upper or lowercase chars.
|
||||
*
|
||||
* @return Encoded chars.
|
||||
*/
|
||||
public static char[] encode(byte[] src, boolean upper) {
|
||||
char[] table = upper ? Base16.upper : Base16.lower;
|
||||
char[] dst = new char[src.length * 2];
|
||||
|
||||
for (int si = 0, di = 0; si < src.length; si++) {
|
||||
byte b = src[si];
|
||||
dst[di++] = table[(b & 0xf0) >>> 4];
|
||||
dst[di++] = table[(b & 0x0f)];
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode base16 chars to bytes.
|
||||
*
|
||||
* @param src Chars to decode.
|
||||
*
|
||||
* @return Decoded bytes.
|
||||
*/
|
||||
public static byte[] decode(char[] src) {
|
||||
byte[] dst = new byte[src.length / 2];
|
||||
|
||||
for (int si = 0, di = 0; di < dst.length; di++) {
|
||||
byte high = decode[src[si++] & 0x7f];
|
||||
byte low = decode[src[si++] & 0x7f];
|
||||
dst[di] = (byte) ((high << 4) + low);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
/**
|
||||
* A key-value pair.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class KeyValue<K, V> {
|
||||
public final K key;
|
||||
public final V value;
|
||||
|
||||
public KeyValue(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
KeyValue<?, ?> that = (KeyValue<?, ?>) o;
|
||||
return key.equals(that.key) && value.equals(that.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * key.hashCode() + value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%s, %s)", key, value);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,223 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.group.ChannelGroup;
|
||||
import io.netty.channel.group.ChannelGroupFuture;
|
||||
import io.netty.channel.group.DefaultChannelGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.codec.Utf8StringCodec;
|
||||
import com.lambdaworks.redis.protocol.Command;
|
||||
import com.lambdaworks.redis.protocol.CommandHandler;
|
||||
import com.lambdaworks.redis.protocol.ConnectionWatchdog;
|
||||
import com.lambdaworks.redis.pubsub.PubSubCommandHandler;
|
||||
import com.lambdaworks.redis.pubsub.RedisPubSubConnection;
|
||||
|
||||
/**
|
||||
* A scalable thread-safe <a href="http://redis.io/">Redis</a> client. Multiple threads
|
||||
* may share one connection provided they avoid blocking and transactional operations
|
||||
* such as BLPOP and MULTI/EXEC.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class RedisClient {
|
||||
|
||||
private Bootstrap bootstrap;
|
||||
private ChannelGroup channels;
|
||||
private long timeout;
|
||||
private TimeUnit unit;
|
||||
private InetSocketAddress addr;
|
||||
|
||||
/**
|
||||
* Create a new client that connects to the supplied host on the default port.
|
||||
*
|
||||
* @param host Server hostname.
|
||||
*/
|
||||
public RedisClient(EventLoopGroup group, String host) {
|
||||
this(group, host, 6379, 60000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new client that connects to the supplied host and port. Connection
|
||||
* attempts and non-blocking commands will {@link #setDefaultTimeout timeout}
|
||||
* after 60 seconds.
|
||||
*
|
||||
* @param host Server hostname.
|
||||
* @param port Server port.
|
||||
*/
|
||||
public RedisClient(EventLoopGroup group, String host, int port, int timeout) {
|
||||
this(group, NioSocketChannel.class, host, port, timeout);
|
||||
}
|
||||
|
||||
public RedisClient(EventLoopGroup group, Class<? extends SocketChannel> socketChannelClass, String host,
|
||||
int port, int timeout) {
|
||||
addr = new InetSocketAddress(host, port);
|
||||
|
||||
bootstrap = new Bootstrap().channel(socketChannelClass).group(group).remoteAddress(addr);
|
||||
|
||||
setDefaultTimeout(timeout, TimeUnit.MILLISECONDS);
|
||||
|
||||
channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default timeout for {@link RedisConnection connections} created by
|
||||
* this client. The timeout applies to connection attempts and non-blocking
|
||||
* commands.
|
||||
*
|
||||
* @param timeout Default connection timeout.
|
||||
* @param unit Unit of time for the timeout.
|
||||
*/
|
||||
public void setDefaultTimeout(long timeout, TimeUnit unit) {
|
||||
this.timeout = timeout;
|
||||
this.unit = unit;
|
||||
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) unit.toMillis(timeout));
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new synchronous connection to the redis server that treats
|
||||
* keys and values as UTF-8 strings.
|
||||
*
|
||||
* @return A new connection.
|
||||
*/
|
||||
public RedisConnection<String, String> connect() {
|
||||
return connect(new Utf8StringCodec());
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new asynchronous connection to the redis server that treats
|
||||
* keys and values as UTF-8 strings.
|
||||
*
|
||||
* @return A new connection.
|
||||
*/
|
||||
public RedisAsyncConnection<String, String> connectAsync() {
|
||||
return connectAsync(new Utf8StringCodec());
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new pub/sub connection to the redis server that treats
|
||||
* keys and values as UTF-8 strings.
|
||||
*
|
||||
* @return A new connection.
|
||||
*/
|
||||
public RedisPubSubConnection<String, String> connectPubSub() {
|
||||
return connectPubSub(new Utf8StringCodec());
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new synchronous connection to the redis server. Use the supplied
|
||||
* {@link RedisCodec codec} to encode/decode keys and values.
|
||||
*
|
||||
* @param codec Use this codec to encode/decode keys and values.
|
||||
*
|
||||
* @return A new connection.
|
||||
*/
|
||||
public <K, V> RedisConnection<K, V> connect(RedisCodec<K, V> codec) {
|
||||
return new RedisConnection<K, V>(connectAsync(codec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new asynchronous connection to the redis server. Use the supplied
|
||||
* {@link RedisCodec codec} to encode/decode keys and values.
|
||||
*
|
||||
* @param codec Use this codec to encode/decode keys and values.
|
||||
*
|
||||
* @return A new connection.
|
||||
*/
|
||||
public <K, V> RedisAsyncConnection<K, V> connectAsync(RedisCodec<K, V> codec) {
|
||||
BlockingQueue<Command<K, V, ?>> queue = new LinkedBlockingQueue<Command<K, V, ?>>();
|
||||
|
||||
CommandHandler<K, V> handler = new CommandHandler<K, V>(queue);
|
||||
RedisAsyncConnection<K, V> connection = new RedisAsyncConnection<K, V>(this, queue, codec, timeout, unit, bootstrap.group());
|
||||
|
||||
return connect(handler, connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new pub/sub connection to the redis server. Use the supplied
|
||||
* {@link RedisCodec codec} to encode/decode keys and values.
|
||||
*
|
||||
* @param codec Use this codec to encode/decode keys and values.
|
||||
*
|
||||
* @return A new pub/sub connection.
|
||||
*/
|
||||
public <K, V> RedisPubSubConnection<K, V> connectPubSub(RedisCodec<K, V> codec) {
|
||||
BlockingQueue<Command<K, V, ?>> queue = new LinkedBlockingQueue<Command<K, V, ?>>();
|
||||
|
||||
PubSubCommandHandler<K, V> handler = new PubSubCommandHandler<K, V>(queue, codec);
|
||||
RedisPubSubConnection<K, V> connection = new RedisPubSubConnection<K, V>(this, queue, codec, timeout, unit, bootstrap.group());
|
||||
|
||||
return connect(handler, connection);
|
||||
}
|
||||
|
||||
private <K, V, T extends RedisAsyncConnection<K, V>> T connect(final CommandHandler<K, V> handler, final T connection) {
|
||||
try {
|
||||
final ConnectionWatchdog watchdog = new ConnectionWatchdog(bootstrap, channels);
|
||||
|
||||
ChannelFuture connect = null;
|
||||
// TODO use better concurrent workaround
|
||||
synchronized (bootstrap) {
|
||||
connect = bootstrap.handler(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ch.pipeline().addLast(watchdog, handler, connection);
|
||||
}
|
||||
}).connect();
|
||||
}
|
||||
connect.sync();
|
||||
|
||||
connection.setReconnect(true);
|
||||
|
||||
return connection;
|
||||
} catch (Throwable e) {
|
||||
throw new RedisConnectionException("Unable to connect " + addr, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown this client and close all open connections. The client should be
|
||||
* discarded after calling shutdown.
|
||||
*/
|
||||
public void shutdown() {
|
||||
ChannelGroupFuture future = shutdownAsync();
|
||||
future.awaitUninterruptibly();
|
||||
}
|
||||
|
||||
public ChannelGroupFuture shutdownAsync() {
|
||||
bootstrap.attr(ConnectionWatchdog.SHUTDOWN_KEY, true);
|
||||
|
||||
for (Channel c : channels) {
|
||||
ChannelPipeline pipeline = c.pipeline();
|
||||
RedisAsyncConnection<?, ?> connection = pipeline.get(RedisAsyncConnection.class);
|
||||
connection.close();
|
||||
}
|
||||
return channels.close();
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddr() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RedisClient [addr=" + addr + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
/**
|
||||
* Exception thrown when the thread executing a redis command is
|
||||
* interrupted.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RedisCommandInterruptedException extends RedisException {
|
||||
public RedisCommandInterruptedException(Throwable e) {
|
||||
super("Command interrupted", e);
|
||||
}
|
||||
}
|
@ -1,871 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.lambdaworks.redis.output.ListScanResult;
|
||||
import com.lambdaworks.redis.protocol.ConnectionWatchdog;
|
||||
|
||||
/**
|
||||
* A synchronous thread-safe connection to a redis server. Multiple threads may
|
||||
* share one {@link RedisConnection} provided they avoid blocking and transactional
|
||||
* operations such as {@link #blpop} and {@link #multi()}/{@link #exec}.
|
||||
*
|
||||
* A {@link ConnectionWatchdog} monitors each connection and reconnects
|
||||
* automatically until {@link #close} is called. All pending commands will be
|
||||
* (re)sent after successful reconnection.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class RedisConnection<K, V> {
|
||||
protected RedisAsyncConnection<K, V> c;
|
||||
protected long timeout;
|
||||
protected TimeUnit unit;
|
||||
|
||||
public RedisClient getRedisClient() {
|
||||
return c.getRedisClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new connection.
|
||||
*
|
||||
* @param c Underlying async connection.
|
||||
*/
|
||||
public RedisConnection(RedisAsyncConnection<K, V> c) {
|
||||
this.c = c;
|
||||
this.timeout = c.timeout;
|
||||
this.unit = c.unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the command timeout for this connection.
|
||||
*
|
||||
* @param timeout Command timeout.
|
||||
* @param unit Unit of time for the timeout.
|
||||
*/
|
||||
public void setTimeout(long timeout, TimeUnit unit) {
|
||||
this.timeout = timeout;
|
||||
this.unit = unit;
|
||||
c.setTimeout(timeout, unit);
|
||||
}
|
||||
|
||||
public Long append(K key, V value) {
|
||||
return await(c.append(key, value));
|
||||
}
|
||||
|
||||
public String auth(String password) {
|
||||
return c.auth(password);
|
||||
}
|
||||
|
||||
public String bgrewriteaof() {
|
||||
return await(c.bgrewriteaof());
|
||||
}
|
||||
|
||||
public String bgsave() {
|
||||
return await(c.bgsave());
|
||||
}
|
||||
|
||||
public Long bitcount(K key) {
|
||||
return await(c.bitcount(key));
|
||||
}
|
||||
|
||||
public Long bitcount(K key, long start, long end) {
|
||||
return await(c.bitcount(key, start, end));
|
||||
}
|
||||
|
||||
public Long bitopAnd(K destination, K... keys) {
|
||||
return await(c.bitopAnd(destination, keys));
|
||||
}
|
||||
|
||||
public Long bitopNot(K destination, K source) {
|
||||
return await(c.bitopNot(destination, source));
|
||||
}
|
||||
|
||||
public Long bitopOr(K destination, K... keys) {
|
||||
return await(c.bitopOr(destination, keys));
|
||||
}
|
||||
public Long bitopXor(K destination, K... keys) {
|
||||
return await(c.bitopXor(destination, keys));
|
||||
}
|
||||
|
||||
public KeyValue<K, V> blpop(long timeout, K... keys) throws InterruptedException {
|
||||
long timeout2 = (timeout == 0 ? Long.MAX_VALUE : max(timeout, unit.toSeconds(this.timeout)));
|
||||
return awaitInterruptibly(c.blpop(timeout, keys), timeout2, SECONDS);
|
||||
}
|
||||
|
||||
public KeyValue<K, V> brpop(long timeout, K... keys) {
|
||||
long timeout2 = (timeout == 0 ? Long.MAX_VALUE : max(timeout, unit.toSeconds(this.timeout)));
|
||||
return await(c.brpop(timeout, keys), timeout2, SECONDS);
|
||||
}
|
||||
|
||||
public V brpoplpush(long timeout, K source, K destination) throws InterruptedException {
|
||||
long timeout2 = (timeout == 0 ? Long.MAX_VALUE : max(timeout, unit.toSeconds(this.timeout)));
|
||||
return awaitInterruptibly(c.brpoplpush(timeout, source, destination), timeout2, SECONDS);
|
||||
}
|
||||
|
||||
public K clientGetname() {
|
||||
return await(c.clientGetname());
|
||||
}
|
||||
|
||||
public String clientSetname(K name) {
|
||||
return await(c.clientSetname(name));
|
||||
}
|
||||
|
||||
public String clientKill(String addr) {
|
||||
return await(c.clientKill(addr));
|
||||
}
|
||||
|
||||
public String clientList() {
|
||||
return await(c.clientList());
|
||||
}
|
||||
|
||||
public List<String> configGet(String parameter) {
|
||||
return await(c.configGet(parameter));
|
||||
}
|
||||
|
||||
public String configResetstat() {
|
||||
return await(c.configResetstat());
|
||||
}
|
||||
|
||||
public String configSet(String parameter, String value) {
|
||||
return await(c.configSet(parameter, value));
|
||||
}
|
||||
|
||||
public Long dbsize() {
|
||||
return await(c.dbsize());
|
||||
}
|
||||
|
||||
public String debugObject(K key) {
|
||||
return await(c.debugObject(key));
|
||||
}
|
||||
|
||||
public Long decr(K key) {
|
||||
return await(c.decr(key));
|
||||
}
|
||||
|
||||
public Long decrby(K key, long amount) {
|
||||
return await(c.decrby(key, amount));
|
||||
}
|
||||
|
||||
public Long del(K... keys) {
|
||||
return await(c.del(keys));
|
||||
}
|
||||
|
||||
public String discard() {
|
||||
return await(c.discard());
|
||||
}
|
||||
|
||||
public byte[] dump(K key) {
|
||||
return await(c.dump(key));
|
||||
}
|
||||
|
||||
public V echo(V msg) {
|
||||
return await(c.echo(msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Eval the supplied script, which must result in the requested
|
||||
* {@link ScriptOutputType type}.
|
||||
*
|
||||
* @param script Lua script to evaluate.
|
||||
* @param type Script output type.
|
||||
* @param keys Redis keys to pass to script.
|
||||
*
|
||||
* @param <T> Expected return type.
|
||||
*
|
||||
* @return The result of evaluating the script.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T eval(V script, ScriptOutputType type, K... keys) {
|
||||
return (T) await(c.eval(script, type, keys, (V[]) new Object[0]));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T eval(V script, ScriptOutputType type, K[] keys, V... values) {
|
||||
return (T) await(c.eval(script, type, keys, values));
|
||||
}
|
||||
|
||||
/**
|
||||
* Eval a pre-loaded script identified by its SHA-1 digest, which must result
|
||||
* in the requested {@link ScriptOutputType type}.
|
||||
*
|
||||
* @param digest Lowercase hex string of script's SHA-1 digest.
|
||||
* @param type Script output type.
|
||||
* @param keys Redis keys to pass to script.
|
||||
*
|
||||
* @param <T> Expected return type.
|
||||
*
|
||||
* @return The result of evaluating the script.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T evalsha(String digest, ScriptOutputType type, K... keys) {
|
||||
return (T) await(c.evalsha(digest, type, keys, (V[]) new Object[0]));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T evalsha(String digest, ScriptOutputType type, K[] keys, V... values) {
|
||||
return (T) await(c.evalsha(digest, type, keys, values));
|
||||
}
|
||||
|
||||
public Long pfadd(K key, V... values) {
|
||||
return await(c.pfadd(key, values));
|
||||
}
|
||||
|
||||
public Long pfcount(K key, K... keys) {
|
||||
return await(c.pfcount(key, keys));
|
||||
}
|
||||
|
||||
public Long pfmerge(K destkey, K... sourceKeys) {
|
||||
return await(c.pfmerge(destkey, sourceKeys));
|
||||
}
|
||||
|
||||
public Boolean exists(K key) {
|
||||
return await(c.exists(key));
|
||||
}
|
||||
|
||||
public Boolean expire(K key, long seconds) {
|
||||
return await(c.expire(key, seconds));
|
||||
}
|
||||
|
||||
public Boolean expireat(K key, Date timestamp) {
|
||||
return await(c.expireat(key, timestamp));
|
||||
}
|
||||
|
||||
public Boolean expireat(K key, long timestamp) {
|
||||
return await(c.expireat(key, timestamp));
|
||||
}
|
||||
|
||||
public List<Object> exec() {
|
||||
return await(c.exec());
|
||||
}
|
||||
|
||||
public String flushall() {
|
||||
return await(c.flushall());
|
||||
}
|
||||
|
||||
public String flushdb() {
|
||||
return await(c.flushdb());
|
||||
}
|
||||
|
||||
public V get(K key) {
|
||||
return await(c.get(key));
|
||||
}
|
||||
|
||||
public Long getbit(K key, long offset) {
|
||||
return await(c.getbit(key, offset));
|
||||
}
|
||||
|
||||
public V getrange(K key, long start, long end) {
|
||||
return await(c.getrange(key, start, end));
|
||||
}
|
||||
|
||||
public V getset(K key, V value) {
|
||||
return await(c.getset(key, value));
|
||||
}
|
||||
|
||||
public Long hdel(K key, K... fields) {
|
||||
return await(c.hdel(key, fields));
|
||||
}
|
||||
|
||||
public Boolean hexists(K key, K field) {
|
||||
return await(c.hexists(key, field));
|
||||
}
|
||||
|
||||
public V hget(K key, K field) {
|
||||
return await(c.hget(key, field));
|
||||
}
|
||||
|
||||
public Long hincrby(K key, K field, long amount) {
|
||||
return await(c.hincrby(key, field, amount));
|
||||
}
|
||||
|
||||
public String hincrbyfloat(K key, K field, String amount) {
|
||||
return await(c.hincrbyfloat(key, field, amount));
|
||||
}
|
||||
|
||||
public Map<K, V> hgetall(K key) {
|
||||
return await(c.hgetall(key));
|
||||
}
|
||||
|
||||
public Set<K> hkeys(K key) {
|
||||
return await(c.hkeys(key));
|
||||
}
|
||||
|
||||
public Long hlen(K key) {
|
||||
return await(c.hlen(key));
|
||||
}
|
||||
|
||||
public List<V> hmget(K key, K... fields) {
|
||||
return await(c.hmget(key, fields));
|
||||
}
|
||||
|
||||
public String hmset(K key, Map<K, V> map) {
|
||||
return await(c.hmset(key, map));
|
||||
}
|
||||
|
||||
public Boolean hset(K key, K field, V value) {
|
||||
return await(c.hset(key, field, value));
|
||||
}
|
||||
|
||||
public Boolean hsetnx(K key, K field, V value) {
|
||||
return await(c.hsetnx(key, field, value));
|
||||
}
|
||||
|
||||
public List<V> hvals(K key) {
|
||||
return await(c.hvals(key));
|
||||
}
|
||||
|
||||
public Long incr(K key) {
|
||||
return await(c.incr(key));
|
||||
}
|
||||
|
||||
public Long incrby(K key, long amount) {
|
||||
return await(c.incrby(key, amount));
|
||||
}
|
||||
|
||||
public String incrbyfloat(K key, String amount) {
|
||||
return await(c.incrbyfloat(key, amount));
|
||||
}
|
||||
|
||||
public String info() {
|
||||
return await(c.info());
|
||||
}
|
||||
|
||||
public String info(String section) {
|
||||
return await(c.info(section));
|
||||
}
|
||||
|
||||
public List<K> keys(K pattern) {
|
||||
return await(c.keys(pattern));
|
||||
}
|
||||
|
||||
public Date lastsave() {
|
||||
return await(c.lastsave());
|
||||
}
|
||||
|
||||
public V lindex(K key, long index) {
|
||||
return await(c.lindex(key, index));
|
||||
}
|
||||
|
||||
public Long linsert(K key, boolean before, V pivot, V value) {
|
||||
return await(c.linsert(key, before, pivot, value));
|
||||
}
|
||||
|
||||
public Long llen(K key) {
|
||||
return await(c.llen(key));
|
||||
}
|
||||
|
||||
public V lpop(K key) {
|
||||
return await(c.lpop(key));
|
||||
}
|
||||
|
||||
public Long lpush(K key, V... values) {
|
||||
return await(c.lpush(key, values));
|
||||
}
|
||||
|
||||
public Long lpushx(K key, V value) {
|
||||
return await(c.lpushx(key, value));
|
||||
}
|
||||
|
||||
public List<V> lrange(K key, long start, long stop) {
|
||||
return await(c.lrange(key, start, stop));
|
||||
}
|
||||
|
||||
public Long lrem(K key, long count, V value) {
|
||||
return await(c.lrem(key, count, value));
|
||||
}
|
||||
|
||||
public String lset(K key, long index, V value) {
|
||||
return await(c.lset(key, index, value));
|
||||
}
|
||||
|
||||
public String ltrim(K key, long start, long stop) {
|
||||
return await(c.ltrim(key, start, stop));
|
||||
}
|
||||
|
||||
public String migrate(String host, int port, K key, int db, long timeout) {
|
||||
return await(c.migrate(host, port, key, db, timeout));
|
||||
}
|
||||
|
||||
public List<V> mget(K... keys) {
|
||||
return await(c.mget(keys));
|
||||
}
|
||||
|
||||
public Boolean move(K key, int db) {
|
||||
return await(c.move(key, db));
|
||||
}
|
||||
|
||||
public String multi() {
|
||||
return await(c.multi());
|
||||
}
|
||||
|
||||
public String mset(Map<K, V> map) {
|
||||
return await(c.mset(map));
|
||||
}
|
||||
|
||||
public Boolean msetnx(Map<K, V> map) {
|
||||
return await(c.msetnx(map));
|
||||
}
|
||||
|
||||
public String objectEncoding(K key) {
|
||||
return await(c.objectEncoding(key));
|
||||
}
|
||||
|
||||
public Long objectIdletime(K key) {
|
||||
return await(c.objectIdletime(key));
|
||||
}
|
||||
|
||||
public Long objectRefcount(K key) {
|
||||
return await(c.objectRefcount(key));
|
||||
}
|
||||
|
||||
public Boolean persist(K key) {
|
||||
return await(c.persist(key));
|
||||
}
|
||||
|
||||
public Boolean pexpire(K key, long milliseconds) {
|
||||
return await(c.pexpire(key, milliseconds));
|
||||
}
|
||||
|
||||
public Boolean pexpireat(K key, Date timestamp) {
|
||||
return await(c.pexpireat(key, timestamp));
|
||||
}
|
||||
|
||||
public Boolean pexpireat(K key, long timestamp) {
|
||||
return await(c.pexpireat(key, timestamp));
|
||||
}
|
||||
|
||||
|
||||
public String ping() {
|
||||
return await(c.ping());
|
||||
}
|
||||
|
||||
public Long pttl(K key) {
|
||||
return await(c.pttl(key));
|
||||
}
|
||||
|
||||
public Long publish(K channel, V message) {
|
||||
return await(c.publish(channel, message));
|
||||
}
|
||||
|
||||
public String quit() {
|
||||
return await(c.quit());
|
||||
}
|
||||
|
||||
public V randomkey() {
|
||||
return await(c.randomkey());
|
||||
}
|
||||
|
||||
public String rename(K key, K newKey) {
|
||||
return await(c.rename(key, newKey));
|
||||
}
|
||||
|
||||
public Boolean renamenx(K key, K newKey) {
|
||||
return await(c.renamenx(key, newKey));
|
||||
}
|
||||
|
||||
public String restore(K key, long ttl, byte[] value) {
|
||||
return await(c.restore(key, ttl, value));
|
||||
}
|
||||
|
||||
public V rpop(K key) {
|
||||
return await(c.rpop(key));
|
||||
}
|
||||
|
||||
public V rpoplpush(K source, K destination) {
|
||||
return await(c.rpoplpush(source, destination));
|
||||
}
|
||||
|
||||
public Long rpush(K key, V... values) {
|
||||
return await(c.rpush(key, values));
|
||||
}
|
||||
|
||||
public Long rpushx(K key, V value) {
|
||||
return await(c.rpushx(key, value));
|
||||
}
|
||||
|
||||
public Long sadd(K key, V... members) {
|
||||
return await(c.sadd(key, members));
|
||||
}
|
||||
|
||||
public String save() {
|
||||
return await(c.save());
|
||||
}
|
||||
|
||||
public Long scard(K key) {
|
||||
return await(c.scard(key));
|
||||
}
|
||||
|
||||
public List<Boolean> scriptExists(String... digests) {
|
||||
return await(c.scriptExists(digests));
|
||||
}
|
||||
|
||||
public String scriptFlush() {
|
||||
return await(c.scriptFlush());
|
||||
}
|
||||
|
||||
public String scriptKill() {
|
||||
return await(c.scriptKill());
|
||||
}
|
||||
|
||||
public String scriptLoad(V script) {
|
||||
return await(c.scriptLoad(script));
|
||||
}
|
||||
|
||||
public Set<V> sdiff(K... keys) {
|
||||
return await(c.sdiff(keys));
|
||||
}
|
||||
|
||||
public Long sdiffstore(K destination, K... keys) {
|
||||
return await(c.sdiffstore(destination, keys));
|
||||
}
|
||||
|
||||
public String select(int db) {
|
||||
return c.select(db);
|
||||
}
|
||||
|
||||
public String set(K key, V value) {
|
||||
return await(c.set(key, value));
|
||||
}
|
||||
|
||||
public Long setbit(K key, long offset, int value) {
|
||||
return await(c.setbit(key, offset, value));
|
||||
}
|
||||
|
||||
public String setex(K key, long seconds, V value) {
|
||||
return await(c.setex(key, seconds, value));
|
||||
}
|
||||
|
||||
public String psetex(K key, long millis, V value) {
|
||||
return await(c.psetex(key, millis, value));
|
||||
}
|
||||
|
||||
public Boolean setnx(K key, V value) {
|
||||
return await(c.setnx(key, value));
|
||||
}
|
||||
|
||||
public String setexnx(K key, V value, long millis) {
|
||||
return await(c.setexnx(key, value, millis));
|
||||
}
|
||||
|
||||
public Long setrange(K key, long offset, V value) {
|
||||
return await(c.setrange(key, offset, value));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void shutdown() {
|
||||
c.shutdown();
|
||||
}
|
||||
|
||||
public void shutdown(boolean save) {
|
||||
c.shutdown(save);
|
||||
}
|
||||
|
||||
public Set<V> sinter(K... keys) {
|
||||
return await(c.sinter(keys));
|
||||
}
|
||||
|
||||
public Long sinterstore(K destination, K... keys) {
|
||||
return await(c.sinterstore(destination, keys));
|
||||
}
|
||||
|
||||
public Boolean sismember(K key, V member) {
|
||||
return await(c.sismember(key, member));
|
||||
}
|
||||
|
||||
public Boolean smove(K source, K destination, V member) {
|
||||
return await(c.smove(source, destination, member));
|
||||
}
|
||||
|
||||
public String slaveof(String host, int port) {
|
||||
return await(c.slaveof(host, port));
|
||||
}
|
||||
|
||||
public String slaveofNoOne() {
|
||||
return await(c.slaveofNoOne());
|
||||
}
|
||||
|
||||
public List<Object> slowlogGet() {
|
||||
return await(c.slowlogGet());
|
||||
}
|
||||
|
||||
public List<Object> slowlogGet(int count) {
|
||||
return await(c.slowlogGet(count));
|
||||
}
|
||||
|
||||
public Long slowlogLen() {
|
||||
return await(c.slowlogLen());
|
||||
}
|
||||
|
||||
public String slowlogReset() {
|
||||
return await(c.slowlogReset());
|
||||
}
|
||||
|
||||
public Set<V> smembers(K key) {
|
||||
return await(c.smembers(key));
|
||||
}
|
||||
|
||||
public List<V> sort(K key) {
|
||||
return await(c.sort(key));
|
||||
}
|
||||
|
||||
public List<V> sort(K key, SortArgs sortArgs) {
|
||||
return await(c.sort(key, sortArgs));
|
||||
}
|
||||
|
||||
public Long sortStore(K key, SortArgs sortArgs, K destination) {
|
||||
return await(c.sortStore(key, sortArgs, destination));
|
||||
}
|
||||
|
||||
public V spop(K key) {
|
||||
return await(c.spop(key));
|
||||
}
|
||||
|
||||
public V srandmember(K key) {
|
||||
return await(c.srandmember(key));
|
||||
}
|
||||
|
||||
public Set<V> srandmember(K key, long count) {
|
||||
return await(c.srandmember(key, count));
|
||||
}
|
||||
|
||||
public Long srem(K key, V... members) {
|
||||
return await(c.srem(key, members));
|
||||
}
|
||||
|
||||
public Set<V> sunion(K... keys) {
|
||||
return await(c.sunion(keys));
|
||||
}
|
||||
|
||||
public Long sunionstore(K destination, K... keys) {
|
||||
return await(c.sunionstore(destination, keys));
|
||||
}
|
||||
|
||||
public String sync() {
|
||||
return await(c.sync());
|
||||
}
|
||||
|
||||
public Long strlen(K key) {
|
||||
return await(c.strlen(key));
|
||||
}
|
||||
|
||||
public Long ttl(K key) {
|
||||
return await(c.ttl(key));
|
||||
}
|
||||
|
||||
public String type(K key) {
|
||||
return await(c.type(key));
|
||||
}
|
||||
|
||||
public String watch(K... keys) {
|
||||
return await(c.watch(keys));
|
||||
}
|
||||
|
||||
public String unwatch() {
|
||||
return await(c.unwatch());
|
||||
}
|
||||
|
||||
public Long zadd(K key, double score, V member) {
|
||||
return await(c.zadd(key, score, member));
|
||||
}
|
||||
|
||||
public Long zadd(K key, Object... scoresAndValues) {
|
||||
return await(c.zadd(key, scoresAndValues));
|
||||
}
|
||||
|
||||
public Long zcard(K key) {
|
||||
return await(c.zcard(key));
|
||||
}
|
||||
|
||||
public Long zcount(K key, double min, double max) {
|
||||
return await(c.zcount(key, min, max));
|
||||
}
|
||||
|
||||
public Long zcount(K key, String min, String max) {
|
||||
return await(c.zcount(key, min, max));
|
||||
}
|
||||
|
||||
public Double zincrby(K key, double amount, K member) {
|
||||
return await(c.zincrby(key, amount, member));
|
||||
}
|
||||
|
||||
public Long zinterstore(K destination, K... keys) {
|
||||
return await(c.zinterstore(destination, keys));
|
||||
}
|
||||
|
||||
public Long zinterstore(K destination, ZStoreArgs storeArgs, K... keys) {
|
||||
return await(c.zinterstore(destination, storeArgs, keys));
|
||||
}
|
||||
|
||||
public List<V> zrange(K key, long start, long stop) {
|
||||
return await(c.zrange(key, start, stop));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrangeWithScores(K key, long start, long stop) {
|
||||
return await(c.zrangeWithScores(key, start, stop));
|
||||
}
|
||||
|
||||
public List<V> zrangebyscore(K key, double min, double max) {
|
||||
return await(c.zrangebyscore(key, min, max));
|
||||
}
|
||||
|
||||
public List<V> zrangebyscore(K key, String min, String max) {
|
||||
return await(c.zrangebyscore(key, min, max));
|
||||
}
|
||||
|
||||
public List<V> zrangebyscore(K key, double min, double max, long offset, long count) {
|
||||
return await(c.zrangebyscore(key, min, max, offset, count));
|
||||
}
|
||||
|
||||
public List<V> zrangebyscore(K key, String min, String max, long offset, long count) {
|
||||
return await(c.zrangebyscore(key, min, max, offset, count));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrangebyscoreWithScores(K key, double min, double max) {
|
||||
return await(c.zrangebyscoreWithScores(key, min, max));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrangebyscoreWithScores(K key, String min, String max) {
|
||||
return await(c.zrangebyscoreWithScores(key, min, max));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrangebyscoreWithScores(K key, double min, double max, long offset, long count) {
|
||||
return await(c.zrangebyscoreWithScores(key, min, max, offset, count));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrangebyscoreWithScores(K key, String min, String max, long offset, long count) {
|
||||
return await(c.zrangebyscoreWithScores(key, min, max, offset, count));
|
||||
}
|
||||
|
||||
public Long zrank(K key, V member) {
|
||||
return await(c.zrank(key, member));
|
||||
}
|
||||
|
||||
public Long zrem(K key, V... members) {
|
||||
return await(c.zrem(key, members));
|
||||
}
|
||||
|
||||
public Long zremrangebyrank(K key, long start, long stop) {
|
||||
return await(c.zremrangebyrank(key, start, stop));
|
||||
}
|
||||
|
||||
public Long zremrangebyscore(K key, double min, double max) {
|
||||
return await(c.zremrangebyscore(key, min, max));
|
||||
}
|
||||
|
||||
public Long zremrangebyscore(K key, String min, String max) {
|
||||
return await(c.zremrangebyscore(key, min, max));
|
||||
}
|
||||
|
||||
public List<String> time() {
|
||||
return await(c.time());
|
||||
}
|
||||
|
||||
public List<V> zrevrange(K key, long start, long stop) {
|
||||
return await(c.zrevrange(key, start, stop));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrevrangeWithScores(K key, long start, long stop) {
|
||||
return await(c.zrevrangeWithScores(key, start, stop));
|
||||
}
|
||||
|
||||
public List<V> zrevrangebyscore(K key, double max, double min) {
|
||||
return await(c.zrevrangebyscore(key, max, min));
|
||||
}
|
||||
|
||||
public List<V> zrevrangebyscore(K key, String max, String min) {
|
||||
return await(c.zrevrangebyscore(key, max, min));
|
||||
}
|
||||
|
||||
public List<V> zrevrangebyscore(K key, double max, double min, long offset, long count) {
|
||||
return await(c.zrevrangebyscore(key, max, min, offset, count));
|
||||
}
|
||||
|
||||
public List<V> zrevrangebyscore(K key, String max, String min, long offset, long count) {
|
||||
return await(c.zrevrangebyscore(key, max, min, offset, count));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrevrangebyscoreWithScores(K key, double max, double min) {
|
||||
return await(c.zrevrangebyscoreWithScores(key, max, min));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrevrangebyscoreWithScores(K key, String max, String min) {
|
||||
return await(c.zrevrangebyscoreWithScores(key, max, min));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrevrangebyscoreWithScores(K key, double max, double min, long offset, long count) {
|
||||
return await(c.zrevrangebyscoreWithScores(key, max, min, offset, count));
|
||||
}
|
||||
|
||||
public List<ScoredValue<V>> zrevrangebyscoreWithScores(K key, String max, String min, long offset, long count) {
|
||||
return await(c.zrevrangebyscoreWithScores(key, max, min, offset, count));
|
||||
}
|
||||
|
||||
public Long zrevrank(K key, V member) {
|
||||
return await(c.zrevrank(key, member));
|
||||
}
|
||||
|
||||
public Double zscore(K key, V member) {
|
||||
return await(c.zscore(key, member));
|
||||
}
|
||||
|
||||
public Long zunionstore(K destination, K... keys) {
|
||||
return await(c.zunionstore(destination, keys));
|
||||
}
|
||||
|
||||
public Long zunionstore(K destination, ZStoreArgs storeArgs, K... keys) {
|
||||
return await(c.zunionstore(destination, storeArgs, keys));
|
||||
}
|
||||
|
||||
public ListScanResult<V> sscan(K key, long startValue) {
|
||||
return await(c.sscan(key, startValue));
|
||||
}
|
||||
|
||||
public ListScanResult<V> zscan(K key, long startValue) {
|
||||
return await(c.zscan(key, startValue));
|
||||
}
|
||||
|
||||
public RedisAsyncConnection<K, V> getAsync() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the connection.
|
||||
*/
|
||||
public void close() {
|
||||
c.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SHA-1 digest for the supplied script.
|
||||
*
|
||||
* @param script Lua script.
|
||||
*
|
||||
* @return Script digest as a lowercase hex string.
|
||||
*/
|
||||
public String digest(V script) {
|
||||
return c.digest(script);
|
||||
}
|
||||
|
||||
private <T> T await(Future<T> future, long timeout, TimeUnit unit) {
|
||||
return c.await(future, timeout, unit);
|
||||
}
|
||||
|
||||
private <T> T awaitInterruptibly(Future<T> future, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return c.awaitInterruptibly(future, timeout, unit);
|
||||
}
|
||||
|
||||
|
||||
private <T> T await(Future<T> future) {
|
||||
return c.await(future, timeout, unit);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
public class RedisConnectionClosedException extends RedisException {
|
||||
|
||||
private static final long serialVersionUID = 1895201562761894967L;
|
||||
|
||||
public RedisConnectionClosedException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
public class RedisConnectionException extends RedisException {
|
||||
|
||||
private static final long serialVersionUID = 4007817232147176510L;
|
||||
|
||||
public RedisConnectionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public RedisConnectionException(String msg, Throwable e) {
|
||||
super(msg, e);
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
/**
|
||||
* Exception thrown when redis returns an error message, or when the client
|
||||
* fails for any reason.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RedisException extends RuntimeException {
|
||||
|
||||
public RedisException() {
|
||||
}
|
||||
|
||||
public RedisException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public RedisException(String msg, Throwable e) {
|
||||
super(msg, e);
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
public class RedisMovedException extends RedisException {
|
||||
|
||||
private static final long serialVersionUID = -6969734163155547631L;
|
||||
|
||||
private int slot;
|
||||
|
||||
public RedisMovedException(int slot) {
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
public class RedisTimeoutException extends RedisException {
|
||||
|
||||
private static final long serialVersionUID = -6969734163155547631L;
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
/**
|
||||
* A value and its associated score from a ZSET.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class ScoredValue<V> {
|
||||
public final double score;
|
||||
public final V value;
|
||||
|
||||
public ScoredValue(double score, V value) {
|
||||
this.score = score;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ScoredValue<?> that = (ScoredValue<?>) o;
|
||||
return Double.compare(that.score, score) == 0 && value.equals(that.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
long temp = score != +0.0d ? Double.doubleToLongBits(score) : 0L;
|
||||
int result = (int) (temp ^ (temp >>> 32));
|
||||
return 31 * result + (value != null ? value.hashCode() : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%f, %s)", score, value);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
/**
|
||||
* A Lua script returns one of the following types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #BOOLEAN} boolean</li>
|
||||
* <li>{@link #INTEGER} 64-bit integer</li>
|
||||
* <li>{@link #STATUS} status string</li>
|
||||
* <li>{@link #VALUE} value</li>
|
||||
* <li>{@link #MAPVALUE} typed value</li>
|
||||
* <li>{@link #MULTI} of these types</li>.
|
||||
* </ul>
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public enum ScriptOutputType {
|
||||
BOOLEAN, INTEGER, MULTI, STATUS, VALUE, MAPVALUE, MAPVALUELIST
|
||||
}
|
||||
|
@ -1,124 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
import com.lambdaworks.redis.protocol.CommandArgs;
|
||||
import com.lambdaworks.redis.protocol.CommandKeyword;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.lambdaworks.redis.protocol.CommandKeyword.*;
|
||||
import static com.lambdaworks.redis.protocol.CommandType.GET;
|
||||
|
||||
/**
|
||||
* Argument list builder for the redis <a href="http://redis.io/commands/sort">SORT</a>
|
||||
* command. Static import the methods from {@link Builder} and chain the method calls:
|
||||
* <code>by("weight_*").desc().limit(0, 2)</code>.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class SortArgs {
|
||||
private String by;
|
||||
private Long offset, count;
|
||||
private List<String> get;
|
||||
private CommandKeyword order;
|
||||
private boolean alpha;
|
||||
|
||||
/**
|
||||
* Static builder methods.
|
||||
*/
|
||||
public static class Builder {
|
||||
public static SortArgs by(String pattern) {
|
||||
return new SortArgs().by(pattern);
|
||||
}
|
||||
|
||||
public static SortArgs limit(long offset, long count) {
|
||||
return new SortArgs().limit(offset, count);
|
||||
}
|
||||
|
||||
public static SortArgs get(String pattern) {
|
||||
return new SortArgs().get(pattern);
|
||||
}
|
||||
|
||||
public static SortArgs asc() {
|
||||
return new SortArgs().asc();
|
||||
}
|
||||
|
||||
public static SortArgs desc() {
|
||||
return new SortArgs().desc();
|
||||
}
|
||||
|
||||
public static SortArgs alpha() {
|
||||
return new SortArgs().alpha();
|
||||
}
|
||||
}
|
||||
|
||||
public SortArgs by(String pattern) {
|
||||
by = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortArgs limit(long offset, long count) {
|
||||
this.offset = offset;
|
||||
this.count = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortArgs get(String pattern) {
|
||||
if (get == null) {
|
||||
get = new ArrayList<String>();
|
||||
}
|
||||
get.add(pattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortArgs asc() {
|
||||
order = ASC;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortArgs desc() {
|
||||
order = DESC;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortArgs alpha() {
|
||||
alpha = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
<K, V> void build(CommandArgs<K, V> args, K store) {
|
||||
|
||||
if (by != null) {
|
||||
args.add(BY);
|
||||
args.add(by);
|
||||
}
|
||||
|
||||
if (get != null) {
|
||||
for (String pattern : get) {
|
||||
args.add(GET);
|
||||
args.add(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
if (offset != null) {
|
||||
args.add(LIMIT);
|
||||
args.add(offset);
|
||||
args.add(count);
|
||||
}
|
||||
|
||||
if (order != null) {
|
||||
args.add(order);
|
||||
}
|
||||
|
||||
if (alpha) {
|
||||
args.add(ALPHA);
|
||||
}
|
||||
|
||||
if (store != null) {
|
||||
args.add(STORE);
|
||||
args.addKey(store);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis;
|
||||
|
||||
import com.lambdaworks.redis.protocol.CommandArgs;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.lambdaworks.redis.protocol.CommandKeyword.*;
|
||||
|
||||
/**
|
||||
* Argument list builder for the redis <a href="http://redis.io/commands/zunionstore">ZUNIONSTORE</a>
|
||||
* and <a href="http://redis.io/commands/zinterstore">ZINTERSTORE</a> commands. Static import the
|
||||
* methods from {@link Builder} and chain the method calls: <code>weights(1, 2).max()</code>.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class ZStoreArgs {
|
||||
private static enum Aggregate { SUM, MIN, MAX }
|
||||
|
||||
private List<Long> weights;
|
||||
private Aggregate aggregate;
|
||||
|
||||
/**
|
||||
* Static builder methods.
|
||||
*/
|
||||
public static class Builder {
|
||||
public static ZStoreArgs weights(long... weights) {
|
||||
return new ZStoreArgs().weights(weights);
|
||||
}
|
||||
|
||||
public static ZStoreArgs sum() {
|
||||
return new ZStoreArgs().sum();
|
||||
}
|
||||
|
||||
public static ZStoreArgs min() {
|
||||
return new ZStoreArgs().min();
|
||||
}
|
||||
|
||||
public static ZStoreArgs max() {
|
||||
return new ZStoreArgs().max();
|
||||
}
|
||||
}
|
||||
|
||||
public ZStoreArgs weights(long... weights) {
|
||||
this.weights = new ArrayList<Long>(weights.length);
|
||||
for (long weight : weights) {
|
||||
this.weights.add(weight);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ZStoreArgs sum() {
|
||||
aggregate = Aggregate.SUM;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ZStoreArgs min() {
|
||||
aggregate = Aggregate.MIN;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ZStoreArgs max() {
|
||||
aggregate = Aggregate.MAX;
|
||||
return this;
|
||||
}
|
||||
|
||||
<K, V> void build(CommandArgs<K, V> args) {
|
||||
if (weights != null) {
|
||||
args.add(WEIGHTS);
|
||||
for (long weight : weights) {
|
||||
args.add(weight);
|
||||
}
|
||||
}
|
||||
|
||||
if (aggregate != null) {
|
||||
args.add(AGGREGATE);
|
||||
switch (aggregate) {
|
||||
case SUM:
|
||||
args.add(SUM);
|
||||
break;
|
||||
case MIN:
|
||||
args.add(MIN);
|
||||
break;
|
||||
case MAX:
|
||||
args.add(MAX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.codec;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* A RedisCodec encodes keys and values sent to redis, and decodes keys
|
||||
* and values in the command output.
|
||||
*
|
||||
* The encode methods will be called by multiple threads and must be thread-safe,
|
||||
* however the decode methods will only be called by one thread.
|
||||
*
|
||||
* @param <K> Key type.
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public abstract class RedisCodec<K, V> {
|
||||
/**
|
||||
* Decode the key output by redis.
|
||||
*
|
||||
* @param bytes Raw bytes of the key.
|
||||
*
|
||||
* @return The decoded key.
|
||||
*/
|
||||
public abstract K decodeKey(ByteBuffer bytes);
|
||||
|
||||
/**
|
||||
* Decode the value output by redis.
|
||||
*
|
||||
* @param bytes Raw bytes of the value.
|
||||
*
|
||||
* @return The decoded value.
|
||||
*/
|
||||
public abstract V decodeValue(ByteBuffer bytes);
|
||||
|
||||
/**
|
||||
* Encode the key for output to redis.
|
||||
*
|
||||
* @param key Key.
|
||||
*
|
||||
* @return The encoded key.
|
||||
*/
|
||||
public abstract byte[] encodeKey(K key);
|
||||
|
||||
/**
|
||||
* Encode the value for output to redis.
|
||||
*
|
||||
* @param value Value.
|
||||
*
|
||||
* @return The encoded value.
|
||||
*/
|
||||
public abstract byte[] encodeValue(V value);
|
||||
|
||||
|
||||
public abstract byte[] encodeMapValue(V value);
|
||||
|
||||
public abstract byte[] encodeMapKey(K key);
|
||||
|
||||
public abstract V decodeMapValue(ByteBuffer bytes);
|
||||
|
||||
public abstract K decodeMapKey(ByteBuffer bytes);
|
||||
|
||||
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.codec;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.*;
|
||||
|
||||
import static java.nio.charset.CoderResult.OVERFLOW;
|
||||
|
||||
/**
|
||||
* A {@link RedisCodec} that handles UTF-8 encoded keys and values.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class Utf8StringCodec extends RedisCodec<String, String> {
|
||||
private Charset charset;
|
||||
private CharsetDecoder decoder;
|
||||
private CharBuffer chars;
|
||||
|
||||
/**
|
||||
* Initialize a new instance that encodes and decodes strings using
|
||||
* the UTF-8 charset;
|
||||
*/
|
||||
public Utf8StringCodec() {
|
||||
charset = Charset.forName("UTF-8");
|
||||
decoder = charset.newDecoder();
|
||||
chars = CharBuffer.allocate(1024);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeKey(ByteBuffer bytes) {
|
||||
return decode(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeValue(ByteBuffer bytes) {
|
||||
return decode(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeKey(String key) {
|
||||
return encode(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeValue(String value) {
|
||||
return encode(value);
|
||||
}
|
||||
|
||||
private String decode(ByteBuffer bytes) {
|
||||
chars.clear();
|
||||
bytes.mark();
|
||||
|
||||
decoder.reset();
|
||||
while (decoder.decode(bytes, chars, true) == OVERFLOW || decoder.flush(chars) == OVERFLOW) {
|
||||
chars = CharBuffer.allocate(chars.capacity() * 2);
|
||||
bytes.reset();
|
||||
}
|
||||
|
||||
return chars.flip().toString();
|
||||
}
|
||||
|
||||
private byte[] encode(String string) {
|
||||
return string.getBytes(charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeMapValue(String value) {
|
||||
return encodeValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeMapKey(String key) {
|
||||
return encodeKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeMapValue(ByteBuffer bytes) {
|
||||
return decodeValue(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeMapKey(ByteBuffer bytes) {
|
||||
return decodeKey(bytes);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link java.util.List} of boolean output.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class BooleanListOutput<K, V> extends CommandOutput<K, V, List<Boolean>> {
|
||||
public BooleanListOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ArrayList<Boolean>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long integer) {
|
||||
output.add((integer == 1) ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Boolean output. The actual value is returned as an integer
|
||||
* where 0 indicates false and 1 indicates true, or as a null
|
||||
* bulk reply for script output.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class BooleanOutput<K, V> extends CommandOutput<K, V, Boolean> {
|
||||
public BooleanOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long integer) {
|
||||
output = (integer == 1) ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output = (bytes != null) ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// Copyright (C) 2012 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Byte array output.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class ByteArrayOutput<K, V> extends CommandOutput<K, V, byte[]> {
|
||||
public ByteArrayOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
if (bytes != null) {
|
||||
output = new byte[bytes.remaining()];
|
||||
bytes.get(output);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Date output with no milliseconds.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class DateOutput<K, V> extends CommandOutput<K, V, Date> {
|
||||
public DateOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long time) {
|
||||
output = new Date(time * 1000);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static java.lang.Double.parseDouble;
|
||||
|
||||
/**
|
||||
* Double output, may be null.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class DoubleOutput<K, V> extends CommandOutput<K, V, Double> {
|
||||
public DoubleOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output = (bytes == null) ? null : parseDouble(decodeAscii(bytes));
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 64-bit integer output, may be null.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class IntegerOutput<K, V> extends CommandOutput<K, V, Long> {
|
||||
public IntegerOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long integer) {
|
||||
output = integer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output = bytes == null ? null : new Long(decodeAscii(bytes));
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link List} of keys output.
|
||||
*
|
||||
* @param <K> Key type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class KeyListOutput<K, V> extends CommandOutput<K, V, List<K>> {
|
||||
public KeyListOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ArrayList<K>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output.add(codec.decodeKey(bytes));
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2013 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Key output.
|
||||
*
|
||||
* @param <K> Key type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class KeyOutput<K, V> extends CommandOutput<K, V, K> {
|
||||
public KeyOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output = (bytes == null) ? null : codec.decodeKey(bytes);
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.KeyValue;
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Key-value pair output.
|
||||
*
|
||||
* @param <K> Key type.
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class KeyValueOutput<K, V> extends CommandOutput<K, V, KeyValue<K, V>> {
|
||||
private K key;
|
||||
|
||||
public KeyValueOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
if (bytes != null) {
|
||||
if (key == null) {
|
||||
key = codec.decodeKey(bytes);
|
||||
} else {
|
||||
V value = codec.decodeValue(bytes);
|
||||
output = new KeyValue<K, V>(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
public class ListMapOutput<K, V> extends CommandOutput<K, V, List<Map<K, V>>> {
|
||||
private K key;
|
||||
private int index = 0;
|
||||
|
||||
public ListMapOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ArrayList<Map<K, V>>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
if (key == null) {
|
||||
key = codec.decodeMapKey(bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
V value = (bytes == null) ? null : codec.decodeMapValue(bytes);
|
||||
if (output.isEmpty()) {
|
||||
output.add(new HashMap<K, V>());
|
||||
}
|
||||
Map<K, V> map = output.get(index);
|
||||
if (map == null) {
|
||||
map = new HashMap<K, V>();
|
||||
output.add(map);
|
||||
}
|
||||
if (map.get(key) != null) {
|
||||
index++;
|
||||
map = new HashMap<K, V>();
|
||||
output.add(map);
|
||||
}
|
||||
map.put(key, value);
|
||||
key = null;
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
public class ListScanOutput<K, V> extends CommandOutput<K, V, ListScanResult<V>> {
|
||||
|
||||
public ListScanOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ListScanResult<V>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
if (output.getPos() == null) {
|
||||
output.setPos(((Number) codec.decodeValue(bytes)).longValue());
|
||||
} else {
|
||||
output.addValue(codec.decodeValue(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ListScanResult<V> {
|
||||
|
||||
private Long pos;
|
||||
private List<V> values = new ArrayList<V>();
|
||||
|
||||
public void setPos(Long pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
public Long getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public void addValue(V value) {
|
||||
values.add(value);
|
||||
}
|
||||
public List<V> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
/**
|
||||
* {@link List} of keys output.
|
||||
*
|
||||
* @param <K> Key type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class MapKeyListOutput<K, V> extends CommandOutput<K, V, Set<K>> {
|
||||
public MapKeyListOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new LinkedHashSet<K>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output.add(codec.decodeMapKey(bytes));
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link Map} of keys and values output.
|
||||
*
|
||||
* @param <K> Key type.
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class MapOutput<K, V> extends CommandOutput<K, V, Map<K, V>> {
|
||||
private K key;
|
||||
|
||||
public MapOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new HashMap<K, V>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
if (key == null) {
|
||||
key = codec.decodeMapKey(bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
V value = (bytes == null) ? null : codec.decodeMapValue(bytes);
|
||||
output.put(key, value);
|
||||
key = null;
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link List} of values output.
|
||||
*
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class MapValueListOutput<K, V> extends CommandOutput<K, V, List<V>> {
|
||||
public MapValueListOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ArrayList<V>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output.add(bytes == null ? null : codec.decodeMapValue(bytes));
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Value output.
|
||||
*
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class MapValueOutput<K, V> extends CommandOutput<K, V, V> {
|
||||
public MapValueOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output = (bytes == null) ? null : codec.decodeMapValue(bytes);
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.RedisException;
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.Command;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Output of all commands within a MULTI block.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class MultiOutput<K, V> extends CommandOutput<K, V, List<Object>> {
|
||||
private Queue<Command<K, V, ?>> queue;
|
||||
|
||||
public MultiOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ArrayList<Object>());
|
||||
queue = new LinkedList<Command<K, V, ?>>();
|
||||
}
|
||||
|
||||
public void add(Command<K, V, ?> cmd) {
|
||||
queue.add(cmd);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
for (Command<K, V, ?> c : queue) {
|
||||
c.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long integer) {
|
||||
queue.peek().getOutput().set(integer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
queue.peek().getOutput().set(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(ByteBuffer error) {
|
||||
CommandOutput<K, V, ?> output = queue.isEmpty() ? this : queue.peek().getOutput();
|
||||
output.setError(decodeAscii(error));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(int depth) {
|
||||
if (depth == 1) {
|
||||
Command<K, V, ?> cmd = queue.remove();
|
||||
CommandOutput<K, V, ?> o = cmd.getOutput();
|
||||
output.add(!o.hasError() ? o.get() : new RedisException(o.getError()));
|
||||
cmd.complete();
|
||||
} else if (depth == 0 && !queue.isEmpty()) {
|
||||
for (Command<K, V, ?> cmd : queue) {
|
||||
cmd.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.RedisException;
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@link List} of command outputs, possibly deeply nested.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class NestedMultiOutput<K, V> extends CommandOutput<K, V, List<Object>> {
|
||||
private LinkedList<List<Object>> stack;
|
||||
private int depth;
|
||||
|
||||
public NestedMultiOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ArrayList<Object>());
|
||||
stack = new LinkedList<List<Object>>();
|
||||
depth = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long integer) {
|
||||
output.add(integer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output.add(bytes == null ? null : codec.decodeKey(bytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(ByteBuffer error) {
|
||||
output.add(new RedisException(decodeAscii(error)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(int depth) {
|
||||
if (depth > this.depth) {
|
||||
Object o = output.remove(output.size() - 1);
|
||||
stack.push(output);
|
||||
output = new ArrayList<Object>();
|
||||
output.add(o);
|
||||
} else if (depth > 0 && depth < this.depth) {
|
||||
stack.peek().add(output);
|
||||
output = stack.pop();
|
||||
}
|
||||
this.depth = depth;
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.ScoredValue;
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link List} of values and their associated scores.
|
||||
*
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class ScoredValueListOutput<K, V> extends CommandOutput<K, V, List<ScoredValue<V>>> {
|
||||
private V value;
|
||||
|
||||
public ScoredValueListOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ArrayList<ScoredValue<V>>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
if (value == null) {
|
||||
value = codec.decodeValue(bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
double score = Double.parseDouble(decodeAscii(bytes));
|
||||
output.add(new ScoredValue<V>(score, value));
|
||||
value = null;
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.lambdaworks.redis.protocol.Charsets.buffer;
|
||||
|
||||
/**
|
||||
* Status message output.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class StatusOutput<K, V> extends CommandOutput<K, V, String> {
|
||||
private static final ByteBuffer OK = buffer("OK");
|
||||
|
||||
public StatusOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output = OK.equals(bytes) ? "OK" : decodeAscii(bytes);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link List} of string output.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class StringListOutput<K, V> extends CommandOutput<K, V, List<String>> {
|
||||
public StringListOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ArrayList<String>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output.add(bytes == null ? null : decodeAscii(bytes));
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link List} of values output.
|
||||
*
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class ValueListOutput<K, V> extends CommandOutput<K, V, List<V>> {
|
||||
public ValueListOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ArrayList<V>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output.add(bytes == null ? null : codec.decodeValue(bytes));
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Value output.
|
||||
*
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class ValueOutput<K, V> extends CommandOutput<K, V, V> {
|
||||
public ValueOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output = (bytes == null) ? null : codec.decodeValue(bytes);
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link Set} of value output.
|
||||
*
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class ValueSetOutput<K, V> extends CommandOutput<K, V, Set<V>> {
|
||||
public ValueSetOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new HashSet<V>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
output.add(bytes == null ? null : codec.decodeMapValue(bytes));
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package com.lambdaworks.redis.output;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
public class ValueSetScanOutput<K, V> extends CommandOutput<K, V, ListScanResult<V>> {
|
||||
|
||||
public ValueSetScanOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, new ListScanResult<V>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuffer bytes) {
|
||||
if (output.getPos() == null) {
|
||||
output.setPos(toLong(bytes));
|
||||
} else {
|
||||
output.addValue(codec.decodeMapValue(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
private Long toLong(ByteBuffer bytes) {
|
||||
return bytes == null ? null : new Long(new String(bytes.array(), bytes.arrayOffset() + bytes.position(), bytes.limit()));
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.protocol;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* {@link Charset}-related utilities.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class Charsets {
|
||||
public static final Charset ASCII = Charset.forName("US-ASCII");
|
||||
|
||||
public static ByteBuffer buffer(String s) {
|
||||
return ByteBuffer.wrap(s.getBytes(ASCII));
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.protocol;
|
||||
|
||||
import com.lambdaworks.redis.RedisException;
|
||||
import com.lambdaworks.redis.RedisMovedException;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
|
||||
/**
|
||||
* A redis command and its result. All successfully executed commands will
|
||||
* eventually return a {@link CommandOutput} object.
|
||||
*
|
||||
* @param <T> Command output type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class Command<K, V, T> {
|
||||
private static final byte[] CRLF = "\r\n".getBytes(Charsets.ASCII);
|
||||
|
||||
private final Promise<T> promise;
|
||||
public final CommandType type;
|
||||
protected CommandArgs<K, V> args;
|
||||
protected final CommandOutput<K, V, T> output;
|
||||
protected int completeAmount;
|
||||
|
||||
/**
|
||||
* Create a new command with the supplied type and args.
|
||||
*
|
||||
* @param type Command type.
|
||||
* @param output Command output.
|
||||
* @param args Command args, if any.
|
||||
* @param multi Flag indicating if MULTI active.
|
||||
*/
|
||||
public Command(CommandType type, CommandOutput<K, V, T> output, CommandArgs<K, V> args, boolean multi, Promise<T> proimse) {
|
||||
this.type = type;
|
||||
this.output = output;
|
||||
this.args = args;
|
||||
this.completeAmount = multi ? 2 : 1;
|
||||
this.promise = proimse;
|
||||
}
|
||||
|
||||
public Promise<T> getPromise() {
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object that holds this command's output.
|
||||
*
|
||||
* @return The command output object.
|
||||
*/
|
||||
public CommandOutput<K, V, T> getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
promise.cancel(true);
|
||||
}
|
||||
|
||||
public void complete() {
|
||||
completeAmount--;
|
||||
if (completeAmount == 0) {
|
||||
Object res = output.get();
|
||||
if (promise.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
if (res instanceof RedisException) {
|
||||
promise.setFailure((Exception)res);
|
||||
} else if (output.hasError()) {
|
||||
if (output.getError().startsWith("MOVED")) {
|
||||
String[] parts = output.getError().split(" ");
|
||||
int slot = Integer.valueOf(parts[1]);
|
||||
promise.setFailure(new RedisMovedException(slot));
|
||||
} else if (output.getError().startsWith("(error) ASK")) {
|
||||
String[] parts = output.getError().split(" ");
|
||||
int slot = Integer.valueOf(parts[2]);
|
||||
promise.setFailure(new RedisMovedException(slot));
|
||||
} else {
|
||||
promise.setFailure(new RedisException(output.getError()));
|
||||
}
|
||||
} else if (output.hasException()) {
|
||||
promise.setFailure(output.getException());
|
||||
} else {
|
||||
promise.setSuccess((T)res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode and write this command to the supplied buffer using the new
|
||||
* <a href="http://redis.io/topics/protocol">Unified Request Protocol</a>.
|
||||
*
|
||||
* @param buf Buffer to write to.
|
||||
*/
|
||||
void encode(ByteBuf buf) {
|
||||
buf.writeByte('*');
|
||||
writeInt(buf, 1 + (args != null ? args.count() : 0));
|
||||
buf.writeBytes(CRLF);
|
||||
buf.writeByte('$');
|
||||
writeInt(buf, type.bytes.length);
|
||||
buf.writeBytes(CRLF);
|
||||
buf.writeBytes(type.bytes);
|
||||
buf.writeBytes(CRLF);
|
||||
if (args != null) {
|
||||
buf.writeBytes(args.buffer());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the textual value of a positive integer to the supplied buffer.
|
||||
*
|
||||
* @param buf Buffer to write to.
|
||||
* @param value Value to write.
|
||||
*/
|
||||
protected static void writeInt(ByteBuf buf, int value) {
|
||||
if (value < 10) {
|
||||
buf.writeByte('0' + value);
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(8);
|
||||
while (value > 0) {
|
||||
int digit = value % 10;
|
||||
sb.append((char) ('0' + digit));
|
||||
value /= 10;
|
||||
}
|
||||
|
||||
for (int i = sb.length() - 1; i >= 0; i--) {
|
||||
buf.writeByte(sb.charAt(i));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,209 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.protocol;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
|
||||
/**
|
||||
* Redis command argument encoder.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class CommandArgs<K, V> {
|
||||
private static final byte[] CRLF = "\r\n".getBytes(Charsets.ASCII);
|
||||
|
||||
private RedisCodec<K, V> codec;
|
||||
private ByteBuffer buffer;
|
||||
private int count;
|
||||
|
||||
public CommandArgs(RedisCodec<K, V> codec) {
|
||||
this.codec = codec;
|
||||
this.buffer = ByteBuffer.allocate(32);
|
||||
}
|
||||
|
||||
public ByteBuffer buffer() {
|
||||
buffer.flip();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public int count() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> addMapKeys(K... keys) {
|
||||
for (K key : keys) {
|
||||
addMapKey(key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> addMapKey(K key) {
|
||||
return write(codec.encodeMapKey(key));
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> addKey(K key) {
|
||||
return write(codec.encodeKey(key));
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> addKeys(List<K> keys) {
|
||||
for (K key : keys) {
|
||||
addKey(key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> addKeys(K... keys) {
|
||||
for (K key : keys) {
|
||||
addKey(key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> addMapValue(V value) {
|
||||
return write(codec.encodeMapValue(value));
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> addValue(V value) {
|
||||
return write(codec.encodeValue(value));
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> addMapValues(V... values) {
|
||||
for (V value : values) {
|
||||
addMapValue(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> addValues(V... values) {
|
||||
for (V value : values) {
|
||||
addValue(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> add(Map<K, V> map) {
|
||||
if (map.size() > 2) {
|
||||
realloc(buffer.capacity() + 16 * map.size());
|
||||
}
|
||||
|
||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||
write(codec.encodeMapKey(entry.getKey()));
|
||||
write(codec.encodeMapValue(entry.getValue()));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> add(String s) {
|
||||
return write(s);
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> add(long n) {
|
||||
return write(Long.toString(n));
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> add(double n) {
|
||||
return write(Double.toString(n));
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> add(byte[] value) {
|
||||
return write(value);
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> add(CommandKeyword keyword) {
|
||||
return write(keyword.bytes);
|
||||
}
|
||||
|
||||
public CommandArgs<K, V> add(CommandType type) {
|
||||
return write(type.bytes);
|
||||
}
|
||||
|
||||
private CommandArgs<K, V> write(byte[] arg) {
|
||||
buffer.mark();
|
||||
|
||||
if (buffer.remaining() < arg.length) {
|
||||
int estimate = buffer.remaining() + arg.length + 10;
|
||||
realloc(max(buffer.capacity() * 2, estimate));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
buffer.put((byte) '$');
|
||||
write(arg.length);
|
||||
buffer.put(CRLF);
|
||||
buffer.put(arg);
|
||||
buffer.put(CRLF);
|
||||
break;
|
||||
} catch (BufferOverflowException e) {
|
||||
buffer.reset();
|
||||
realloc(buffer.capacity() * 2);
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
return this;
|
||||
}
|
||||
|
||||
private CommandArgs<K, V> write(String arg) {
|
||||
int length = arg.length();
|
||||
|
||||
buffer.mark();
|
||||
|
||||
if (buffer.remaining() < length) {
|
||||
int estimate = buffer.remaining() + length + 10;
|
||||
realloc(max(buffer.capacity() * 2, estimate));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
buffer.put((byte) '$');
|
||||
write(length);
|
||||
buffer.put(CRLF);
|
||||
for (int i = 0; i < length; i++) {
|
||||
buffer.put((byte) arg.charAt(i));
|
||||
}
|
||||
buffer.put(CRLF);
|
||||
break;
|
||||
} catch (BufferOverflowException e) {
|
||||
buffer.reset();
|
||||
realloc(buffer.capacity() * 2);
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void write(long value) {
|
||||
if (value < 10) {
|
||||
buffer.put((byte) ('0' + value));
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(8);
|
||||
while (value > 0) {
|
||||
long digit = value % 10;
|
||||
sb.append((char) ('0' + digit));
|
||||
value /= 10;
|
||||
}
|
||||
|
||||
for (int i = sb.length() - 1; i >= 0; i--) {
|
||||
buffer.put((byte) sb.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void realloc(int size) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(size);
|
||||
this.buffer.flip();
|
||||
buffer.put(this.buffer);
|
||||
buffer.mark();
|
||||
this.buffer = buffer;
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufProcessor;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* A netty {@link ChannelHandler} responsible for writing redis commands and
|
||||
* reading responses from the server.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class CommandHandler<K, V> extends ChannelDuplexHandler {
|
||||
protected BlockingQueue<Command<K, V, ?>> queue;
|
||||
protected ByteBuf buffer;
|
||||
protected RedisStateMachine<K, V> rsm;
|
||||
|
||||
/**
|
||||
* Initialize a new instance that handles commands from the supplied queue.
|
||||
*
|
||||
* @param queue The command queue.
|
||||
*/
|
||||
public CommandHandler(BlockingQueue<Command<K, V, ?>> queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
|
||||
buffer = ctx.alloc().heapBuffer();
|
||||
rsm = new RedisStateMachine<K, V>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
|
||||
buffer.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
ByteBuf input = (ByteBuf) msg;
|
||||
try {
|
||||
if (!input.isReadable()) return;
|
||||
|
||||
// System.out.println("in: " + toHexString(input));
|
||||
|
||||
buffer.discardReadBytes();
|
||||
buffer.writeBytes(input);
|
||||
|
||||
decode(ctx, buffer);
|
||||
} finally {
|
||||
input.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
Command<?, ?, ?> cmd = (Command<?, ?, ?>) msg;
|
||||
ByteBuf buf = ctx.alloc().heapBuffer();
|
||||
cmd.encode(buf);
|
||||
// System.out.println("out: " + toHexString(buf));
|
||||
|
||||
ctx.write(buf, promise);
|
||||
}
|
||||
|
||||
private String toHexString(ByteBuf buf) {
|
||||
final StringBuilder builder = new StringBuilder(buf.readableBytes() * 2);
|
||||
buf.forEachByte(new ByteBufProcessor() {
|
||||
@Override
|
||||
public boolean process(byte value) throws Exception {
|
||||
char b = (char) value;
|
||||
if ((b < ' ' && b != '\n' && b != '\r') || b > '~') {
|
||||
builder.append("\\x").append(StringUtil.byteToHexStringPadded(value));
|
||||
} else {
|
||||
builder.append(b);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer) throws InterruptedException {
|
||||
while (true) {
|
||||
Command<K, V, ?> cmd = queue.peek();
|
||||
if (cmd == null
|
||||
|| !rsm.decode(buffer, cmd.getOutput())) {
|
||||
break;
|
||||
}
|
||||
|
||||
cmd = queue.take();
|
||||
cmd.complete();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.protocol;
|
||||
|
||||
/**
|
||||
* Keyword modifiers for redis commands.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public enum CommandKeyword {
|
||||
AFTER, AGGREGATE, ALPHA, AND, ASC, BEFORE, BY, COUNT, DESC, ENCODING, FLUSH,
|
||||
GETNAME, IDLETIME, KILL, LEN, LIMIT, LIST, LOAD, MAX, MIN, NO, NOSAVE, NOT,
|
||||
ONE, OR, REFCOUNT, RESET, RESETSTAT, SETNAME, STORE, SUM, WEIGHTS,
|
||||
WITHSCORES, XOR, NODES;
|
||||
|
||||
public byte[] bytes;
|
||||
|
||||
private CommandKeyword() {
|
||||
bytes = name().getBytes(Charsets.ASCII);
|
||||
}
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.protocol;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Abstract representation of the output of a redis command.
|
||||
*
|
||||
* @param <T> Output type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public abstract class CommandOutput<K, V, T> {
|
||||
protected RedisCodec<K, V> codec;
|
||||
protected T output;
|
||||
protected String error;
|
||||
protected Throwable exception;
|
||||
|
||||
/**
|
||||
* Initialize a new instance that encodes and decodes keys and
|
||||
* values using the supplied codec.
|
||||
*
|
||||
* @param codec Codec used to encode/decode keys and values.
|
||||
* @param output Initial value of output.
|
||||
*/
|
||||
public CommandOutput(RedisCodec<K, V> codec, T output) {
|
||||
this.codec = codec;
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command output.
|
||||
*
|
||||
* @return The command output.
|
||||
*/
|
||||
public T get() {
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the command output to a sequence of bytes, or null. Concrete
|
||||
* {@link CommandOutput} implementations must override this method
|
||||
* unless they only receive an integer value which cannot be null.
|
||||
*
|
||||
* @param bytes The command output, or null.
|
||||
*/
|
||||
public void set(ByteBuffer bytes) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the command output to a 64-bit signed integer. Concrete
|
||||
* {@link CommandOutput} implementations must override this method
|
||||
* unless they only receive a byte array value.
|
||||
*
|
||||
* @param integer The command output.
|
||||
*/
|
||||
public void set(long integer) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set command output to an error message from the server.
|
||||
*
|
||||
* @param error Error message.
|
||||
*/
|
||||
public void setError(ByteBuffer error) {
|
||||
this.error = decodeAscii(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set command output to an error message from the client.
|
||||
*
|
||||
* @param error Error message.
|
||||
*/
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the command resulted in an error.
|
||||
*
|
||||
* @return true if command resulted in an error.
|
||||
*/
|
||||
public boolean hasError() {
|
||||
return this.error != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error that occurred.
|
||||
*
|
||||
* @return The error.
|
||||
*/
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set exception that was caught while processing result in command output.
|
||||
*
|
||||
* @param exception Exception caught while processing command result.
|
||||
*/
|
||||
public void setException(Throwable exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the processing command result resulted in an exception.
|
||||
*
|
||||
* @return true if processing of command result resulted in an exception.
|
||||
*/
|
||||
public boolean hasException() {
|
||||
return this.exception != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the exception that occurred while processing command result.
|
||||
*
|
||||
* @return The exception.
|
||||
*/
|
||||
public Throwable getException () {
|
||||
return exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the command output complete.
|
||||
*
|
||||
* @param depth Remaining depth of output queue.
|
||||
*/
|
||||
public void complete(int depth) {
|
||||
// nothing to do by default
|
||||
}
|
||||
|
||||
protected String decodeAscii(ByteBuffer bytes) {
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
char[] chars = new char[bytes.remaining()];
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
chars[i] = (char) bytes.get();
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.protocol;
|
||||
|
||||
/**
|
||||
* Redis commands.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public enum CommandType {
|
||||
// Connection
|
||||
|
||||
AUTH, ECHO, PING, QUIT, SELECT,
|
||||
|
||||
// Server
|
||||
|
||||
BGREWRITEAOF, BGSAVE, CLIENT, CONFIG, DBSIZE, DEBUG, FLUSHALL,
|
||||
FLUSHDB, INFO, LASTSAVE, MONITOR, SAVE, SHUTDOWN, SLAVEOF,
|
||||
SLOWLOG, SYNC,
|
||||
|
||||
// Keys
|
||||
|
||||
DEL, DUMP, EXISTS, EXPIRE, EXPIREAT, KEYS, MIGRATE, MOVE, OBJECT, PERSIST,
|
||||
PEXPIRE, PEXPIREAT, PTTL, RANDOMKEY, RENAME, RENAMENX, RESTORE, TTL, TYPE,
|
||||
|
||||
// String
|
||||
|
||||
APPEND, GET, GETRANGE, GETSET, MGET, MSET, MSETNX, SET, SETEX, SETNX,
|
||||
SETRANGE, STRLEN, PSETEX,
|
||||
|
||||
// Numeric
|
||||
|
||||
DECR, DECRBY, INCR, INCRBY, INCRBYFLOAT,
|
||||
|
||||
// List
|
||||
|
||||
BLPOP, BRPOP, BRPOPLPUSH,
|
||||
LINDEX, LINSERT, LLEN, LPOP, LPUSH, LPUSHX, LRANGE, LREM, LSET, LTRIM,
|
||||
RPOP, RPOPLPUSH, RPUSH, RPUSHX, SORT,
|
||||
|
||||
// Hash
|
||||
|
||||
HDEL, HEXISTS, HGET, HGETALL, HINCRBY, HINCRBYFLOAT, HKEYS, HLEN,
|
||||
HMGET, HMSET, HSET, HSETNX, HVALS,
|
||||
|
||||
// Transaction
|
||||
|
||||
DISCARD, EXEC, MULTI, UNWATCH, WATCH,
|
||||
|
||||
// Pub/Sub
|
||||
|
||||
PSUBSCRIBE, PUBLISH, PUNSUBSCRIBE, SUBSCRIBE, UNSUBSCRIBE,
|
||||
|
||||
// Sets
|
||||
|
||||
SADD, SCARD, SDIFF, SDIFFSTORE, SINTER, SINTERSTORE, SISMEMBER,
|
||||
SMEMBERS, SMOVE, SPOP, SRANDMEMBER, SREM, SUNION, SUNIONSTORE,
|
||||
|
||||
// Sorted Set
|
||||
|
||||
ZADD, ZCARD, ZCOUNT, ZINCRBY, ZINTERSTORE, ZRANGE, ZRANGEBYSCORE,
|
||||
ZRANK, ZREM, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZREVRANGE,
|
||||
ZREVRANGEBYSCORE, ZREVRANK, ZSCORE, ZUNIONSTORE,
|
||||
|
||||
TIME,
|
||||
|
||||
// Scripting
|
||||
|
||||
EVAL, EVALSHA, SCRIPT,
|
||||
|
||||
// Bits
|
||||
|
||||
BITCOUNT, BITOP, GETBIT, SETBIT,
|
||||
|
||||
// HyperLogLog
|
||||
PFADD, PFCOUNT, PFMERGE,
|
||||
|
||||
SENTINEL,
|
||||
|
||||
SSCAN, ZSCAN, HSCAN,
|
||||
|
||||
ASKING, CLUSTER;
|
||||
|
||||
public byte[] bytes;
|
||||
|
||||
private CommandType() {
|
||||
bytes = name().getBytes(Charsets.ASCII);
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.protocol;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.group.ChannelGroup;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.lambdaworks.redis.RedisAsyncConnection;
|
||||
|
||||
/**
|
||||
* A netty {@link ChannelHandler} responsible for monitoring the channel and
|
||||
* reconnecting when the connection is lost.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class ConnectionWatchdog extends ChannelInboundHandlerAdapter{
|
||||
|
||||
public static final AttributeKey<Boolean> SHUTDOWN_KEY = AttributeKey.valueOf("shutdown");
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private Bootstrap bootstrap;
|
||||
private Channel channel;
|
||||
private ChannelGroup channels;
|
||||
private static final int BACKOFF_CAP = 12;
|
||||
|
||||
/**
|
||||
* Create a new watchdog that adds to new connections to the supplied {@link ChannelGroup}
|
||||
* and establishes a new {@link Channel} when disconnected, while reconnect is true.
|
||||
*
|
||||
* @param bootstrap Configuration for new channels.
|
||||
*/
|
||||
public ConnectionWatchdog(Bootstrap bootstrap, ChannelGroup channels) {
|
||||
this.bootstrap = bootstrap;
|
||||
this.channels = channels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
channel = ctx.channel();
|
||||
channels.add(channel);
|
||||
ctx.fireChannelActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
ChannelPipeline pipeLine = channel.pipeline();
|
||||
CommandHandler<?, ?> handler = pipeLine.get(CommandHandler.class);
|
||||
RedisAsyncConnection<?, ?> connection = pipeLine.get(RedisAsyncConnection.class);
|
||||
if (connection.isReconnect()) {
|
||||
EventLoop loop = ctx.channel().eventLoop();
|
||||
reconnect(loop, handler, connection);
|
||||
}
|
||||
ctx.fireChannelInactive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconnect to the remote address that the closed channel was connected to.
|
||||
* This creates a new {@link ChannelPipeline} with the same handler instances
|
||||
* contained in the old channel's pipeline.
|
||||
*
|
||||
* @param loop EventLoop
|
||||
* @param handler Redis Command handle.
|
||||
* @param connection RedisAsyncConnection
|
||||
*
|
||||
* @throws Exception when reconnection fails.
|
||||
*/
|
||||
private void reconnect(final EventLoop loop, final CommandHandler<?, ?> handler, final RedisAsyncConnection<?, ?> connection){
|
||||
loop.schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doReConnect(loop, handler, connection, 1);
|
||||
}
|
||||
}, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void doReConnect(final EventLoop loop, final CommandHandler<?, ?> handler, final RedisAsyncConnection<?, ?> connection, final int attempts) {
|
||||
if (!connection.isReconnect()) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("trying to reconnect {}", connection.getRedisClient().getAddr());
|
||||
|
||||
ChannelFuture connect;
|
||||
synchronized (bootstrap) {
|
||||
connect = bootstrap.handler(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ch.pipeline().addLast(ConnectionWatchdog.this, handler, connection);
|
||||
}
|
||||
}).connect();
|
||||
}
|
||||
|
||||
connect.addListener(new GenericFutureListener<ChannelFuture>() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.channel().attr(SHUTDOWN_KEY).get() != null) {
|
||||
future.channel().pipeline().remove(ConnectionWatchdog.this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!future.isSuccess()) {
|
||||
if (!connection.isReconnect()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int timeout = 2 << attempts;
|
||||
loop.schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doReConnect(loop, handler, connection, Math.min(BACKOFF_CAP, attempts + 1));
|
||||
}
|
||||
}, timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
ctx.channel().close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " - bootstrap: " + bootstrap;
|
||||
}
|
||||
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.protocol;
|
||||
|
||||
import com.lambdaworks.redis.RedisException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import static com.lambdaworks.redis.protocol.Charsets.buffer;
|
||||
import static com.lambdaworks.redis.protocol.RedisStateMachine.State.Type.*;
|
||||
|
||||
/**
|
||||
* State machine that decodes redis server responses encoded according to the
|
||||
* <a href="http://redis.io/topics/protocol">Unified Request Protocol</a>.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class RedisStateMachine<K, V> {
|
||||
private static final ByteBuffer QUEUED = buffer("QUEUED");
|
||||
|
||||
static class State {
|
||||
enum Type { SINGLE, ERROR, INTEGER, BULK, MULTI, BYTES }
|
||||
Type type = null;
|
||||
int count = -1;
|
||||
}
|
||||
|
||||
private LinkedList<State> stack;
|
||||
|
||||
/**
|
||||
* Initialize a new instance.
|
||||
*/
|
||||
public RedisStateMachine() {
|
||||
stack = new LinkedList<State>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to decode a redis response and return a flag indicating whether a complete
|
||||
* response was read.
|
||||
*
|
||||
* @param buffer Buffer containing data from the server.
|
||||
* @param output Current command output.
|
||||
*
|
||||
* @return true if a complete response was read.
|
||||
*/
|
||||
public boolean decode(ByteBuf buffer, CommandOutput<K, V, ?> output) {
|
||||
int length, end;
|
||||
ByteBuffer bytes;
|
||||
|
||||
if (stack.isEmpty()) {
|
||||
stack.add(new State());
|
||||
}
|
||||
|
||||
if (output == null) {
|
||||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
loop:
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
State state = stack.peek();
|
||||
|
||||
if (state.type == null) {
|
||||
if (!buffer.isReadable()) break;
|
||||
state.type = readReplyType(buffer);
|
||||
buffer.markReaderIndex();
|
||||
}
|
||||
|
||||
switch (state.type) {
|
||||
case SINGLE:
|
||||
if ((bytes = readLine(buffer)) == null) break loop;
|
||||
if (!QUEUED.equals(bytes)) {
|
||||
setCommandOutputSafely(output, bytes);
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
if ((bytes = readLine(buffer)) == null) break loop;
|
||||
output.setError(bytes);
|
||||
break;
|
||||
case INTEGER:
|
||||
if ((end = findLineEnd(buffer)) == -1) break loop;
|
||||
setCommandOutputSafely(output, readLong(buffer, buffer.readerIndex(), end));
|
||||
break;
|
||||
case BULK:
|
||||
if ((end = findLineEnd(buffer)) == -1) break loop;
|
||||
length = (int) readLong(buffer, buffer.readerIndex(), end);
|
||||
if (length == -1) {
|
||||
setCommandOutputSafely(output, null);
|
||||
} else {
|
||||
state.type = BYTES;
|
||||
state.count = length + 2;
|
||||
buffer.markReaderIndex();
|
||||
continue loop;
|
||||
}
|
||||
break;
|
||||
case MULTI:
|
||||
if (state.count == -1) {
|
||||
if ((end = findLineEnd(buffer)) == -1) break loop;
|
||||
length = (int) readLong(buffer, buffer.readerIndex(), end);
|
||||
state.count = length;
|
||||
buffer.markReaderIndex();
|
||||
}
|
||||
|
||||
if (state.count <= 0) break;
|
||||
|
||||
state.count--;
|
||||
stack.addFirst(new State());
|
||||
continue loop;
|
||||
case BYTES:
|
||||
if ((bytes = readBytes(buffer, state.count)) == null) break loop;
|
||||
setCommandOutputSafely(output, bytes);
|
||||
}
|
||||
|
||||
buffer.markReaderIndex();
|
||||
stack.remove();
|
||||
output.complete(stack.size());
|
||||
}
|
||||
|
||||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
private int findLineEnd(ByteBuf buffer) {
|
||||
int start = buffer.readerIndex();
|
||||
int index = buffer.indexOf(start, buffer.writerIndex(), (byte) '\n');
|
||||
return (index > 0 && buffer.getByte(index - 1) == '\r') ? index : -1;
|
||||
}
|
||||
|
||||
private State.Type readReplyType(ByteBuf buffer) {
|
||||
switch (buffer.readByte()) {
|
||||
case '+': return SINGLE;
|
||||
case '-': return ERROR;
|
||||
case ':': return INTEGER;
|
||||
case '$': return BULK;
|
||||
case '*': return MULTI;
|
||||
default: throw new RedisException("Invalid first byte");
|
||||
}
|
||||
}
|
||||
|
||||
private long readLong(ByteBuf buffer, int start, int end) {
|
||||
long value = 0;
|
||||
|
||||
boolean negative = buffer.getByte(start) == '-';
|
||||
int offset = negative ? start + 1 : start;
|
||||
while (offset < end - 1) {
|
||||
int digit = buffer.getByte(offset++) - '0';
|
||||
value = value * 10 - digit;
|
||||
}
|
||||
if (!negative) value = -value;
|
||||
buffer.readerIndex(end + 1);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private ByteBuffer readLine(ByteBuf buffer) {
|
||||
ByteBuffer bytes = null;
|
||||
int end = findLineEnd(buffer);
|
||||
if (end > -1) {
|
||||
int start = buffer.readerIndex();
|
||||
bytes = buffer.nioBuffer(start, end - start - 1);
|
||||
buffer.readerIndex(end + 1);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private ByteBuffer readBytes(ByteBuf buffer, int count) {
|
||||
ByteBuffer bytes = null;
|
||||
if (buffer.readableBytes() >= count) {
|
||||
bytes = buffer.nioBuffer(buffer.readerIndex(), count - 2);
|
||||
buffer.readerIndex(buffer.readerIndex() + count);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
private boolean setCommandOutputSafely(CommandOutput output, ByteBuffer bytes) {
|
||||
boolean success = false;
|
||||
try {
|
||||
output.set(bytes);
|
||||
success = true;
|
||||
} catch (Throwable t) {
|
||||
output.setException(t);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private boolean setCommandOutputSafely(CommandOutput output, long value) {
|
||||
boolean success = false;
|
||||
try {
|
||||
output.set(value);
|
||||
success = true;
|
||||
} catch (Throwable t) {
|
||||
output.setException(t);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.pubsub;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.*;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.*;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* A netty {@link ChannelHandler} responsible for writing redis pub/sub commands
|
||||
* and reading the response stream from the server.
|
||||
*
|
||||
* @param <K> Key type.
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class PubSubCommandHandler<K, V> extends CommandHandler<K, V> {
|
||||
private RedisCodec<K, V> codec;
|
||||
private PubSubOutput<K, V> output;
|
||||
|
||||
/**
|
||||
* Initialize a new instance.
|
||||
*
|
||||
* @param queue Command queue.
|
||||
* @param codec Codec.
|
||||
*/
|
||||
public PubSubCommandHandler(BlockingQueue<Command<K, V, ?>> queue, RedisCodec<K, V> codec) {
|
||||
super(queue);
|
||||
this.codec = codec;
|
||||
this.output = new PubSubOutput<K, V>(codec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer) throws InterruptedException {
|
||||
while (output.type() == null && !queue.isEmpty()) {
|
||||
CommandOutput<K, V, ?> output = queue.peek().getOutput();
|
||||
if (!rsm.decode(buffer, output)) {
|
||||
return;
|
||||
}
|
||||
queue.take().complete();
|
||||
if (output instanceof PubSubOutput && ((PubSubOutput) output).type() != null) {
|
||||
ctx.fireChannelRead(output);
|
||||
}
|
||||
}
|
||||
|
||||
while (rsm.decode(buffer, output)) {
|
||||
ctx.fireChannelRead(output);
|
||||
output = new PubSubOutput<K, V>(codec);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.pubsub;
|
||||
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.CommandOutput;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* One element of the redis pub/sub stream. May be a message or notification
|
||||
* of subscription details.
|
||||
*
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class PubSubOutput<K, V> extends CommandOutput<K, V, V> {
|
||||
enum Type { message, pmessage, psubscribe, punsubscribe, subscribe, unsubscribe }
|
||||
|
||||
private Type type;
|
||||
private String channel;
|
||||
private String pattern;
|
||||
private long count;
|
||||
|
||||
public PubSubOutput(RedisCodec<K, V> codec) {
|
||||
super(codec, null);
|
||||
}
|
||||
|
||||
public Type type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String channel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public String pattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public long count() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("fallthrough")
|
||||
public void set(ByteBuffer bytes) {
|
||||
if (type == null) {
|
||||
type = Type.valueOf(decodeAscii(bytes));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case pmessage:
|
||||
if (pattern == null) {
|
||||
pattern = decodeAscii(bytes);
|
||||
break;
|
||||
}
|
||||
case message:
|
||||
if (channel == null) {
|
||||
channel = decodeAscii(bytes);
|
||||
break;
|
||||
}
|
||||
if (channel.startsWith("__keyspace@")
|
||||
|| channel.startsWith("__keyevent@")) {
|
||||
output = (V)decodeAscii(bytes);
|
||||
} else {
|
||||
output = codec.decodeValue(bytes);
|
||||
}
|
||||
break;
|
||||
case psubscribe:
|
||||
case punsubscribe:
|
||||
pattern = decodeAscii(bytes);
|
||||
break;
|
||||
case subscribe:
|
||||
case unsubscribe:
|
||||
channel = decodeAscii(bytes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long integer) {
|
||||
count = integer;
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.pubsub;
|
||||
|
||||
/**
|
||||
* Convenience adapter with an empty implementation of all
|
||||
* {@link RedisPubSubListener} callback methods.
|
||||
*
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class RedisPubSubAdapter<V> implements RedisPubSubListener<V> {
|
||||
@Override
|
||||
public void message(String channel, V message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void message(String pattern, String channel, V message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribed(String channel, long count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void psubscribed(String pattern, long count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribed(String channel, long count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void punsubscribed(String pattern, long count) {
|
||||
}
|
||||
}
|
@ -1,174 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.pubsub;
|
||||
|
||||
import static com.lambdaworks.redis.protocol.CommandType.PSUBSCRIBE;
|
||||
import static com.lambdaworks.redis.protocol.CommandType.PUNSUBSCRIBE;
|
||||
import static com.lambdaworks.redis.protocol.CommandType.SUBSCRIBE;
|
||||
import static com.lambdaworks.redis.protocol.CommandType.UNSUBSCRIBE;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.lambdaworks.redis.RedisAsyncConnection;
|
||||
import com.lambdaworks.redis.RedisClient;
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
import com.lambdaworks.redis.protocol.Command;
|
||||
import com.lambdaworks.redis.protocol.CommandArgs;
|
||||
|
||||
/**
|
||||
* An asynchronous thread-safe pub/sub connection to a redis server. After one or
|
||||
* more channels are subscribed to only pub/sub related commands or {@link #quit}
|
||||
* may be called.
|
||||
*
|
||||
* Incoming messages and results of the {@link #subscribe}/{@link #unsubscribe}
|
||||
* calls will be passed to all registered {@link RedisPubSubListener}s.
|
||||
*
|
||||
* A {@link com.lambdaworks.redis.protocol.ConnectionWatchdog} monitors each
|
||||
* connection and reconnects automatically until {@link #close} is called. Channel
|
||||
* and pattern subscriptions are renewed after reconnecting.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public class RedisPubSubConnection<K, V> extends RedisAsyncConnection<K, V> {
|
||||
private final Queue<RedisPubSubListener<V>> listeners = new ConcurrentLinkedQueue<RedisPubSubListener<V>>();
|
||||
private Set<String> channels;
|
||||
private Set<String> patterns;
|
||||
|
||||
/**
|
||||
* Initialize a new connection.
|
||||
*
|
||||
* @param queue Command queue.
|
||||
* @param codec Codec used to encode/decode keys and values.
|
||||
* @param timeout Maximum time to wait for a responses.
|
||||
* @param unit Unit of time for the timeout.
|
||||
* @param eventLoopGroup
|
||||
*/
|
||||
public RedisPubSubConnection(RedisClient client, BlockingQueue<Command<K, V, ?>> queue, RedisCodec<K, V> codec, long timeout, TimeUnit unit, EventLoopGroup eventLoopGroup) {
|
||||
super(client, queue, codec, timeout, unit, eventLoopGroup);
|
||||
channels = new HashSet<String>();
|
||||
patterns = new HashSet<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new listener.
|
||||
*
|
||||
* @param listener Listener.
|
||||
*/
|
||||
public void addListener(RedisPubSubListener<V> listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing listener.
|
||||
*
|
||||
* @param listener Listener.
|
||||
*/
|
||||
public void removeListener(RedisPubSubListener<V> listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void psubscribe(String... patterns) {
|
||||
dispatch(PSUBSCRIBE, new PubSubOutput<K, V>(codec), args(patterns));
|
||||
}
|
||||
|
||||
public void subscribe(String... channels) {
|
||||
dispatch(SUBSCRIBE, new PubSubOutput<K, V>(codec), args(channels));
|
||||
}
|
||||
|
||||
public Future<V> unsubscribe(String... channels) {
|
||||
return dispatch(UNSUBSCRIBE, new PubSubOutput<K, V>(codec), args(channels));
|
||||
}
|
||||
|
||||
public Future<V> punsubscribe(String... patterns) {
|
||||
return dispatch(PUNSUBSCRIBE, new PubSubOutput<K, V>(codec), args(patterns));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelActive(ctx);
|
||||
|
||||
if (channels.size() > 0) {
|
||||
subscribe(channels.toArray(new String[channels.size()]));
|
||||
channels.clear();
|
||||
}
|
||||
|
||||
if (patterns.size() > 0) {
|
||||
psubscribe(toArray(patterns));
|
||||
patterns.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
PubSubOutput<K, V> output = (PubSubOutput<K, V>) msg;
|
||||
|
||||
// Update internal state
|
||||
switch (output.type()) {
|
||||
case psubscribe:
|
||||
patterns.add(output.pattern());
|
||||
break;
|
||||
case punsubscribe:
|
||||
patterns.remove(output.pattern());
|
||||
break;
|
||||
case subscribe:
|
||||
channels.add(output.channel());
|
||||
break;
|
||||
case unsubscribe:
|
||||
channels.remove(output.channel());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// notify watchers, if any
|
||||
for (RedisPubSubListener<V> listener : listeners) {
|
||||
switch (output.type()) {
|
||||
case message:
|
||||
listener.message(output.channel(), output.get());
|
||||
break;
|
||||
case pmessage:
|
||||
listener.message(output.pattern(), output.channel(), output.get());
|
||||
break;
|
||||
case psubscribe:
|
||||
listener.psubscribed(output.pattern(), output.count());
|
||||
break;
|
||||
case punsubscribe:
|
||||
listener.punsubscribed(output.pattern(), output.count());
|
||||
break;
|
||||
case subscribe:
|
||||
listener.subscribed(output.channel(), output.count());
|
||||
break;
|
||||
case unsubscribe:
|
||||
listener.unsubscribed(output.channel(), output.count());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CommandArgs<K, V> args(String... keys) {
|
||||
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
|
||||
for (String key : keys) {
|
||||
args.add(key.toString());
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T[] toArray(Collection<T> c) {
|
||||
Class<T> cls = (Class<T>) c.iterator().next().getClass();
|
||||
T[] array = (T[]) Array.newInstance(cls, c.size());
|
||||
return c.toArray(array);
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.lambdaworks.redis.pubsub;
|
||||
|
||||
/**
|
||||
* Interface for redis pub/sub listeners.
|
||||
*
|
||||
* @param <V> Value type.
|
||||
*
|
||||
* @author Will Glozer
|
||||
*/
|
||||
public interface RedisPubSubListener<V> {
|
||||
/**
|
||||
* Message received from a channel subscription.
|
||||
*
|
||||
* @param channel Channel.
|
||||
* @param message Message.
|
||||
*/
|
||||
void message(String channel, V message);
|
||||
|
||||
/**
|
||||
* Message received from a pattern subscription.
|
||||
*
|
||||
* @param pattern Pattern.
|
||||
* @param channel Channel.
|
||||
* @param message Message.
|
||||
*/
|
||||
void message(String pattern, String channel, V message);
|
||||
|
||||
/**
|
||||
* Subscribed to a channel.
|
||||
*
|
||||
* @param channel Channel
|
||||
* @param count Subscription count.
|
||||
*/
|
||||
void subscribed(String channel, long count);
|
||||
|
||||
/**
|
||||
* Subscribed to a pattern.
|
||||
*
|
||||
* @param pattern Pattern.
|
||||
* @param count Subscription count.
|
||||
*/
|
||||
void psubscribed(String pattern, long count);
|
||||
|
||||
/**
|
||||
* Unsubscribed from a channel.
|
||||
*
|
||||
* @param channel Channel
|
||||
* @param count Subscription count.
|
||||
*/
|
||||
void unsubscribed(String channel, long count);
|
||||
|
||||
/**
|
||||
* Unsubscribed from a pattern.
|
||||
*
|
||||
* @param pattern Channel
|
||||
* @param count Subscription count.
|
||||
*/
|
||||
void punsubscribed(String pattern, long count);
|
||||
}
|
Loading…
Reference in New Issue