// Copyright (C) 2011 - Will Glozer. All rights reserved.
package com.lambdaworks.redis;
import com.lambdaworks.codec.Base16;
import com.lambdaworks.redis.codec.RedisCodec;
import com.lambdaworks.redis.output.*;
import com.lambdaworks.redis.protocol.*;
import org.jboss.netty.channel.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.*;
import static com.lambdaworks.redis.protocol.CommandKeyword.*;
import static com.lambdaworks.redis.protocol.CommandType.*;
/**
* An asynchronous thread-safe connection to a redis server. Multiple threads may
* share one {@link RedisAsyncConnection} 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 RedisAsyncConnection<K, V> extends SimpleChannelUpstreamHandler {
protected BlockingQueue<Command<K, V, ?>> queue;
protected RedisCodec<K, V> codec;
protected Channel channel;
protected long timeout;
protected TimeUnit unit;
protected MultiOutput<K, V> multi;
private String password;
private int db;
private boolean closed;
/**
* 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 response.
* @param unit Unit of time for the timeout.
*/
public RedisAsyncConnection(BlockingQueue<Command<K, V, ?>> queue, RedisCodec<K, V> codec, long timeout, TimeUnit unit) {
this.queue = queue;
this.codec = codec;
this.timeout = timeout;
this.unit = 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;
}
public Future<Long> append(K key, V value) {
return dispatch(APPEND, new IntegerOutput<K, V>(codec), key, value);
}
public String auth(String password) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(password);
Command<K, V, String> cmd = dispatch(AUTH, new StatusOutput<K, V>(codec), args);
String status = await(cmd, timeout, unit);
if ("OK".equals(status)) this.password = password;
return status;
}
public Future<String> bgrewriteaof() {
return dispatch(BGREWRITEAOF, new StatusOutput<K, V>(codec));
}
public Future<String> bgsave() {
return dispatch(BGSAVE, new StatusOutput<K, V>(codec));
}
public Future<Long> bitcount(K key) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key);
return dispatch(BITCOUNT, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> bitcount(K key, long start, long end) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(start).add(end);
return dispatch(BITCOUNT, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> bitopAnd(K destination, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.add(AND).addKey(destination).addKeys(keys);
return dispatch(BITOP, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> bitopNot(K destination, K source) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.add(NOT).addKey(destination).addKey(source);
return dispatch(BITOP, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> bitopOr(K destination, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.add(OR).addKey(destination).addKeys(keys);
return dispatch(BITOP, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> bitopXor(K destination, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.add(XOR).addKey(destination).addKeys(keys);
return dispatch(BITOP, new IntegerOutput<K, V>(codec), args);
}
public Future<KeyValue<K, V>> blpop(long timeout, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKeys(keys).add(timeout);
return dispatch(BLPOP, new KeyValueOutput<K, V>(codec), args);
}
public Future<KeyValue<K, V>> brpop(long timeout, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKeys(keys).add(timeout);
return dispatch(BRPOP, new KeyValueOutput<K, V>(codec), args);
}
public Future<V> brpoplpush(long timeout, K source, K destination) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(source).addKey(destination).add(timeout);
return dispatch(BRPOPLPUSH, new ValueOutput<K, V>(codec), args);
}
public Future<K> clientGetname() {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(GETNAME);
return dispatch(CLIENT, new KeyOutput<K, V>(codec), args);
}
public Future<String> clientSetname(K name) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(SETNAME).addKey(name);
return dispatch(CLIENT, new StatusOutput<K, V>(codec), args);
}
public Future<String> clientKill(String addr) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(KILL).add(addr);
return dispatch(CLIENT, new StatusOutput<K, V>(codec), args);
}
public Future<String> clientList() {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(LIST);
return dispatch(CLIENT, new StatusOutput<K, V>(codec), args);
}
public Future<List<String>> configGet(String parameter) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(GET).add(parameter);
return dispatch(CONFIG, new StringListOutput<K, V>(codec), args);
}
public Future<String> configResetstat() {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(RESETSTAT);
return dispatch(CONFIG, new StatusOutput<K, V>(codec), args);
}
public Future<String> configSet(String parameter, String value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(SET).add(parameter).add(value);
return dispatch(CONFIG, new StatusOutput<K, V>(codec), args);
}
public Future<Long> dbsize() {
return dispatch(DBSIZE, new IntegerOutput<K, V>(codec));
}
public Future<String> debugObject(K key) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(OBJECT).addKey(key);
return dispatch(DEBUG, new StatusOutput<K, V>(codec), args);
}
public Future<Long> decr(K key) {
return dispatch(DECR, new IntegerOutput<K, V>(codec), key);
}
public Future<Long> decrby(K key, long amount) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(amount);
return dispatch(DECRBY, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> del(K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKeys(keys);
return dispatch(DEL, new IntegerOutput<K, V>(codec), args);
}
public Future<String> discard() {
if (multi != null) {
multi.cancel();
multi = null;
}
return dispatch(DISCARD, new StatusOutput<K, V>(codec));
}
public Future<byte[]> dump(K key) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key);
return dispatch(DUMP, new ByteArrayOutput<K, V>(codec), args);
}
public Future<V> echo(V msg) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addValue(msg);
return dispatch(ECHO, new ValueOutput<K, V>(codec), args);
}
public <T> Future<T> eval(V script, ScriptOutputType type, K[] keys, V... values) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addValue(script).add(keys.length).addKeys(keys).addValues(values);
CommandOutput<K, V, T> output = newScriptOutput(codec, type);
return dispatch(EVAL, output, args);
}
public <T> Future<T> evalsha(String digest, ScriptOutputType type, K[] keys, V... values) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.add(digest).add(keys.length).addKeys(keys).addValues(values);
CommandOutput<K, V, T> output = newScriptOutput(codec, type);
return dispatch(EVALSHA, output, args);
}
public Future<Boolean> exists(K key) {
return dispatch(EXISTS, new BooleanOutput<K, V>(codec), key);
}
public Future<Boolean> expire(K key, long seconds) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(seconds);
return dispatch(EXPIRE, new BooleanOutput<K, V>(codec), args);
}
public Future<Boolean> expireat(K key, Date timestamp) {
return expireat(key, timestamp.getTime() / 1000);
}
public Future<Boolean> expireat(K key, long timestamp) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(timestamp);
return dispatch(EXPIREAT, new BooleanOutput<K, V>(codec), args);
}
public Future<List<Object>> exec() {
MultiOutput<K, V> multi = this.multi;
this.multi = null;
if (multi == null) multi = new MultiOutput<K, V>(codec);
return dispatch(EXEC, multi);
}
public Future<String> flushall() throws Exception {
return dispatch(FLUSHALL, new StatusOutput<K, V>(codec));
}
public Future<String> flushdb() throws Exception {
return dispatch(FLUSHDB, new StatusOutput<K, V>(codec));
}
public Future<V> get(K key) {
return dispatch(GET, new ValueOutput<K, V>(codec), key);
}
public Future<Long> getbit(K key, long offset) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(offset);
return dispatch(GETBIT, new IntegerOutput<K, V>(codec), args);
}
public Future<V> getrange(K key, long start, long end) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(start).add(end);
return dispatch(GETRANGE, new ValueOutput<K, V>(codec), args);
}
public Future<V> getset(K key, V value) {
return dispatch(GETSET, new ValueOutput<K, V>(codec), key, value);
}
public Future<Long> hdel(K key, K... fields) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKeys(fields);
return dispatch(HDEL, new IntegerOutput<K, V>(codec), args);
}
public Future<Boolean> hexists(K key, K field) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKey(field);
return dispatch(HEXISTS, new BooleanOutput<K, V>(codec), args);
}
public Future<V> hget(K key, K field) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKey(field);
return dispatch(HGET, new ValueOutput<K, V>(codec), args);
}
public Future<Long> hincrby(K key, K field, long amount) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKey(field).add(amount);
return dispatch(HINCRBY, new IntegerOutput<K, V>(codec), args);
}
public Future<Double> hincrbyfloat(K key, K field, double amount) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKey(field).add(amount);
return dispatch(HINCRBYFLOAT, new DoubleOutput<K, V>(codec), args);
}
public Future<Map<K, V>> hgetall(K key) {
return dispatch(HGETALL, new MapOutput<K, V>(codec), key);
}
public Future<List<K>> hkeys(K key) {
return dispatch(HKEYS, new KeyListOutput<K, V>(codec), key);
}
public Future<Long> hlen(K key) {
return dispatch(HLEN, new IntegerOutput<K, V>(codec), key);
}
public Future<List<V>> hmget(K key, K... fields) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKeys(fields);
return dispatch(HMGET, new ValueListOutput<K, V>(codec), args);
}
public Future<String> hmset(K key, Map<K, V> map) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(map);
return dispatch(HMSET, new StatusOutput<K, V>(codec), args);
}
public Future<Boolean> hset(K key, K field, V value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKey(field).addValue(value);
return dispatch(HSET, new BooleanOutput<K, V>(codec), args);
}
public Future<Boolean> hsetnx(K key, K field, V value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKey(field).addValue(value);
return dispatch(HSETNX, new BooleanOutput<K, V>(codec), args);
}
public Future<List<V>> hvals(K key) {
return dispatch(HVALS, new ValueListOutput<K, V>(codec), key);
}
public Future<Long> incr(K key) {
return dispatch(INCR, new IntegerOutput<K, V>(codec), key);
}
public Future<Long> incrby(K key, long amount) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(amount);
return dispatch(INCRBY, new IntegerOutput<K, V>(codec), args);
}
public Future<Double> incrbyfloat(K key, double amount) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(amount);
return dispatch(INCRBYFLOAT, new DoubleOutput<K, V>(codec), args);
}
public Future<String> info() {
return dispatch(INFO, new StatusOutput<K, V>(codec));
}
public Future<String> info(String section) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(section);
return dispatch(INFO, new StatusOutput<K, V>(codec), args);
}
public Future<List<K>> keys(K pattern) {
return dispatch(KEYS, new KeyListOutput<K, V>(codec), pattern);
}
public Future<Date> lastsave() {
return dispatch(LASTSAVE, new DateOutput<K, V>(codec));
}
public Future<V> lindex(K key, long index) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(index);
return dispatch(LINDEX, new ValueOutput<K, V>(codec), args);
}
public Future<Long> linsert(K key, boolean before, V pivot, V value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(before ? BEFORE : AFTER).addValue(pivot).addValue(value);
return dispatch(LINSERT, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> llen(K key) {
return dispatch(LLEN, new IntegerOutput<K, V>(codec), key);
}
public Future<V> lpop(K key) {
return dispatch(LPOP, new ValueOutput<K, V>(codec), key);
}
public Future<Long> lpush(K key, V... values) {
return dispatch(LPUSH, new IntegerOutput<K, V>(codec), key, values);
}
public Future<Long> lpushx(K key, V value) {
return dispatch(LPUSHX, new IntegerOutput<K, V>(codec), key, value);
}
public Future<List<V>> lrange(K key, long start, long stop) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(start).add(stop);
return dispatch(LRANGE, new ValueListOutput<K, V>(codec), args);
}
public Future<Long> lrem(K key, long count, V value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(count).addValue(value);
return dispatch(LREM, new IntegerOutput<K, V>(codec), args);
}
public Future<String> lset(K key, long index, V value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(index).addValue(value);
return dispatch(LSET, new StatusOutput<K, V>(codec), args);
}
public Future<String> ltrim(K key, long start, long stop) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(start).add(stop);
return dispatch(LTRIM, new StatusOutput<K, V>(codec), args);
}
public Future<String> migrate(String host, int port, K key, int db, long timeout) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.add(host).add(port).addKey(key).add(db).add(timeout);
return dispatch(MIGRATE, new StatusOutput<K, V>(codec), args);
}
public Future<List<V>> mget(K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKeys(keys);
return dispatch(MGET, new ValueListOutput<K, V>(codec), args);
}
public Future<Boolean> move(K key, int db) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(db);
return dispatch(MOVE, new BooleanOutput<K, V>(codec), args);
}
public Future<String> multi() {
Command<K, V, String> cmd = dispatch(MULTI, new StatusOutput<K, V>(codec));
multi = (multi == null ? new MultiOutput<K, V>(codec) : multi);
return cmd;
}
public Future<String> mset(Map<K, V> map) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(map);
return dispatch(MSET, new StatusOutput<K, V>(codec), args);
}
public Future<Boolean> msetnx(Map<K, V> map) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(map);
return dispatch(MSETNX, new BooleanOutput<K, V>(codec), args);
}
public Future<String> objectEncoding(K key) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(ENCODING).addKey(key);
return dispatch(OBJECT, new StatusOutput<K, V>(codec), args);
}
public Future<Long> objectIdletime(K key) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(IDLETIME).addKey(key);
return dispatch(OBJECT, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> objectRefcount(K key) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(REFCOUNT).addKey(key);
return dispatch(OBJECT, new IntegerOutput<K, V>(codec), args);
}
public Future<Boolean> persist(K key) {
return dispatch(PERSIST, new BooleanOutput<K, V>(codec), key);
}
public Future<Boolean> pexpire(K key, long milliseconds) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(milliseconds);
return dispatch(PEXPIRE, new BooleanOutput<K, V>(codec), args);
}
public Future<Boolean> pexpireat(K key, Date timestamp) {
return pexpireat(key, timestamp.getTime());
}
public Future<Boolean> pexpireat(K key, long timestamp) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(timestamp);
return dispatch(PEXPIREAT, new BooleanOutput<K, V>(codec), args);
}
public Future<String> ping() {
return dispatch(PING, new StatusOutput<K, V>(codec));
}
public Future<Long> pttl(K key) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key);
return dispatch(PTTL, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> publish(K channel, V message) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(channel).addValue(message);
return dispatch(PUBLISH, new IntegerOutput<K, V>(codec), args);
}
public Future<String> quit() {
return dispatch(QUIT, new StatusOutput<K, V>(codec));
}
public Future<V> randomkey() {
return dispatch(RANDOMKEY, new ValueOutput<K, V>(codec));
}
public Future<String> rename(K key, K newKey) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKey(newKey);
return dispatch(RENAME, new StatusOutput<K, V>(codec), args);
}
public Future<Boolean> renamenx(K key, K newKey) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addKey(newKey);
return dispatch(RENAMENX, new BooleanOutput<K, V>(codec), args);
}
public Future<String> restore(K key, long ttl, byte[] value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(ttl).add(value);
return dispatch(RESTORE, new StatusOutput<K, V>(codec), args);
}
public Future<V> rpop(K key) {
return dispatch(RPOP, new ValueOutput<K, V>(codec), key);
}
public Future<V> rpoplpush(K source, K destination) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(source).addKey(destination);
return dispatch(RPOPLPUSH, new ValueOutput<K, V>(codec), args);
}
public Future<Long> rpush(K key, V... values) {
return dispatch(RPUSH, new IntegerOutput<K, V>(codec), key, values);
}
public Future<Long> rpushx(K key, V value) {
return dispatch(RPUSHX, new IntegerOutput<K, V>(codec), key, value);
}
public Future<Long> sadd(K key, V... members) {
return dispatch(SADD, new IntegerOutput<K, V>(codec), key, members);
}
public Future<String> save() {
return dispatch(SAVE, new StatusOutput<K, V>(codec));
}
public Future<Long> scard(K key) {
return dispatch(SCARD, new IntegerOutput<K, V>(codec), key);
}
public Future<List<Boolean>> scriptExists(String... digests) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(EXISTS);
for (String sha : digests) args.add(sha);
return dispatch(SCRIPT, new BooleanListOutput<K, V>(codec), args);
}
public Future<String> scriptFlush() {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(FLUSH);
return dispatch(SCRIPT, new StatusOutput<K, V>(codec), args);
}
public Future<String> scriptKill() {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(KILL);
return dispatch(SCRIPT, new StatusOutput<K, V>(codec), args);
}
public Future<String> scriptLoad(V script) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(LOAD).addValue(script);
return dispatch(SCRIPT, new StatusOutput<K, V>(codec), args);
}
public Future<Set<V>> sdiff(K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKeys(keys);
return dispatch(SDIFF, new ValueSetOutput<K, V>(codec), args);
}
public Future<Long> sdiffstore(K destination, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(destination).addKeys(keys);
return dispatch(SDIFFSTORE, new IntegerOutput<K, V>(codec), args);
}
public String select(int db) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(db);
Command<K, V, String> cmd = dispatch(SELECT, new StatusOutput<K, V>(codec), args);
String status = await(cmd, timeout, unit);
if ("OK".equals(status)) this.db = db;
return status;
}
public Future<String> set(K key, V value) {
return dispatch(SET, new StatusOutput<K, V>(codec), key, value);
}
public Future<Long> setbit(K key, long offset, int value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(offset).add(value);
return dispatch(SETBIT, new IntegerOutput<K, V>(codec), args);
}
public Future<String> setex(K key, long seconds, V value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(seconds).addValue(value);
return dispatch(SETEX, new StatusOutput<K, V>(codec), args);
}
public Future<Boolean> setnx(K key, V value) {
return dispatch(SETNX, new BooleanOutput<K, V>(codec), key, value);
}
public Future<Long> setrange(K key, long offset, V value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(offset).addValue(value);
return dispatch(SETRANGE, new IntegerOutput<K, V>(codec), args);
}
@Deprecated
public void shutdown() {
dispatch(SHUTDOWN, new StatusOutput<K, V>(codec));
}
public void shutdown(boolean save) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
dispatch(SHUTDOWN, new StatusOutput<K, V>(codec), save ? args.add(SAVE) : args.add(NOSAVE));
}
public Future<Set<V>> sinter(K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKeys(keys);
return dispatch(SINTER, new ValueSetOutput<K, V>(codec), args);
}
public Future<Long> sinterstore(K destination, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(destination).addKeys(keys);
return dispatch(SINTERSTORE, new IntegerOutput<K, V>(codec), args);
}
public Future<Boolean> sismember(K key, V member) {
return dispatch(SISMEMBER, new BooleanOutput<K, V>(codec), key, member);
}
public Future<Boolean> smove(K source, K destination, V member) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(source).addKey(destination).addValue(member);
return dispatch(SMOVE, new BooleanOutput<K, V>(codec), args);
}
public Future<String> slaveof(String host, int port) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(host).add(port);
return dispatch(SLAVEOF, new StatusOutput<K, V>(codec), args);
}
public Future<String> slaveofNoOne() {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(NO).add(ONE);
return dispatch(SLAVEOF, new StatusOutput<K, V>(codec), args);
}
public Future<List<Object>> slowlogGet() {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(GET);
return dispatch(SLOWLOG, new NestedMultiOutput<K, V>(codec), args);
}
public Future<List<Object>> slowlogGet(int count) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(GET).add(count);
return dispatch(SLOWLOG, new NestedMultiOutput<K, V>(codec), args);
}
public Future<Long> slowlogLen() {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(LEN);
return dispatch(SLOWLOG, new IntegerOutput<K, V>(codec), args);
}
public Future<String> slowlogReset() {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(RESET);
return dispatch(SLOWLOG, new StatusOutput<K, V>(codec), args);
}
public Future<Set<V>> smembers(K key) {
return dispatch(SMEMBERS, new ValueSetOutput<K, V>(codec), key);
}
public Future<List<V>> sort(K key) {
return dispatch(SORT, new ValueListOutput<K, V>(codec), key);
}
public Future<List<V>> sort(K key, SortArgs sortArgs) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key);
sortArgs.build(args, null);
return dispatch(SORT, new ValueListOutput<K, V>(codec), args);
}
public Future<Long> sortStore(K key, SortArgs sortArgs, K destination) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key);
sortArgs.build(args, destination);
return dispatch(SORT, new IntegerOutput<K, V>(codec), args);
}
public Future<V> spop(K key) {
return dispatch(SPOP, new ValueOutput<K, V>(codec), key);
}
public Future<V> srandmember(K key) {
return dispatch(SRANDMEMBER, new ValueOutput<K, V>(codec), key);
}
public Future<Set<V>> srandmember(K key, long count) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(count);
return dispatch(SRANDMEMBER, new ValueSetOutput<K, V>(codec), args);
}
public Future<Long> srem(K key, V... members) {
return dispatch(SREM, new IntegerOutput<K, V>(codec), key, members);
}
public Future<Set<V>> sunion(K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKeys(keys);
return dispatch(SUNION, new ValueSetOutput<K, V>(codec), args);
}
public Future<Long> sunionstore(K destination, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(destination).addKeys(keys);
return dispatch(SUNIONSTORE, new IntegerOutput<K, V>(codec), args);
}
public Future<String> sync() {
return dispatch(SYNC, new StatusOutput<K, V>(codec));
}
public Future<Long> strlen(K key) {
return dispatch(STRLEN, new IntegerOutput<K, V>(codec), key);
}
public Future<Long> ttl(K key) {
return dispatch(TTL, new IntegerOutput<K, V>(codec), key);
}
public Future<String> type(K key) {
return dispatch(TYPE, new StatusOutput<K, V>(codec), key);
}
public Future<String> watch(K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKeys(keys);
return dispatch(WATCH, new StatusOutput<K, V>(codec), args);
}
public Future<String> unwatch() {
return dispatch(UNWATCH, new StatusOutput<K, V>(codec));
}
public Future<Long> zadd(K key, double score, V member) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(score).addValue(member);
return dispatch(ZADD, new IntegerOutput<K, V>(codec), args);
}
@SuppressWarnings("unchecked")
public Future<Long> zadd(K key, Object... scoresAndValues) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key);
for (int i = 0; i < scoresAndValues.length; i += 2) {
args.add((Double) scoresAndValues[i]);
args.addValue((V) scoresAndValues[i + 1]);
}
return dispatch(ZADD, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> zcard(K key) {
return dispatch(ZCARD, new IntegerOutput<K, V>(codec), key);
}
public Future<Long> zcount(K key, double min, double max) {
return zcount(key, string(min), string(max));
}
public Future<Long> zcount(K key, String min, String max) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(min).add(max);
return dispatch(ZCOUNT, new IntegerOutput<K, V>(codec), args);
}
public Future<Double> zincrby(K key, double amount, K member) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(amount).addKey(member);
return dispatch(ZINCRBY, new DoubleOutput<K, V>(codec), args);
}
public Future<Long> zinterstore(K destination, K... keys) {
return zinterstore(destination, new ZStoreArgs(), keys);
}
public Future<Long> zinterstore(K destination, ZStoreArgs storeArgs, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(destination).add(keys.length).addKeys(keys);
storeArgs.build(args);
return dispatch(ZINTERSTORE, new IntegerOutput<K, V>(codec), args);
}
public Future<List<V>> zrange(K key, long start, long stop) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(start).add(stop);
return dispatch(ZRANGE, new ValueListOutput<K, V>(codec), args);
}
public Future<List<ScoredValue<V>>> zrangeWithScores(K key, long start, long stop) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(start).add(stop).add(WITHSCORES);
return dispatch(ZRANGE, new ScoredValueListOutput<K, V>(codec), args);
}
public Future<List<V>> zrangebyscore(K key, double min, double max) {
return zrangebyscore(key, string(min), string(max));
}
public Future<List<V>> zrangebyscore(K key, String min, String max) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(min).add(max);
return dispatch(ZRANGEBYSCORE, new ValueListOutput<K, V>(codec), args);
}
public Future<List<V>> zrangebyscore(K key, double min, double max, long offset, long count) {
return zrangebyscore(key, string(min), string(max), offset, count);
}
public Future<List<V>> zrangebyscore(K key, String min, String max, long offset, long count) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(min).add(max).add(LIMIT).add(offset).add(count);
return dispatch(ZRANGEBYSCORE, new ValueListOutput<K, V>(codec), args);
}
public Future<List<ScoredValue<V>>> zrangebyscoreWithScores(K key, double min, double max) {
return zrangebyscoreWithScores(key, string(min), string(max));
}
public Future<List<ScoredValue<V>>> zrangebyscoreWithScores(K key, String min, String max) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(min).add(max).add(WITHSCORES);
return dispatch(ZRANGEBYSCORE, new ScoredValueListOutput<K, V>(codec), args);
}
public Future<List<ScoredValue<V>>> zrangebyscoreWithScores(K key, double min, double max, long offset, long count) {
return zrangebyscoreWithScores(key, string(min), string(max), offset, count);
}
public Future<List<ScoredValue<V>>> zrangebyscoreWithScores(K key, String min, String max, long offset, long count) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(min).add(max).add(WITHSCORES).add(LIMIT).add(offset).add(count);
return dispatch(ZRANGEBYSCORE, new ScoredValueListOutput<K, V>(codec), args);
}
public Future<Long> zrank(K key, V member) {
return dispatch(ZRANK, new IntegerOutput<K, V>(codec), key, member);
}
public Future<Long> zrem(K key, V... members) {
return dispatch(ZREM, new IntegerOutput<K, V>(codec), key, members);
}
public Future<Long> zremrangebyrank(K key, long start, long stop) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(start).add(stop);
return dispatch(ZREMRANGEBYRANK, new IntegerOutput<K, V>(codec), args);
}
public Future<Long> zremrangebyscore(K key, double min, double max) {
return zremrangebyscore(key, string(min), string(max));
}
public Future<Long> zremrangebyscore(K key, String min, String max) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(min).add(max);
return dispatch(ZREMRANGEBYSCORE, new IntegerOutput<K, V>(codec), args);
}
public Future<List<V>> zrevrange(K key, long start, long stop) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(start).add(stop);
return dispatch(ZREVRANGE, new ValueListOutput<K, V>(codec), args);
}
public Future<List<ScoredValue<V>>> zrevrangeWithScores(K key, long start, long stop) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(start).add(stop).add(WITHSCORES);
return dispatch(ZREVRANGE, new ScoredValueListOutput<K, V>(codec), args);
}
public Future<List<V>> zrevrangebyscore(K key, double max, double min) {
return zrevrangebyscore(key, string(max), string(min));
}
public Future<List<V>> zrevrangebyscore(K key, String max, String min) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).add(max).add(min);
return dispatch(ZREVRANGEBYSCORE, new ValueListOutput<K, V>(codec), args);
}
public Future<List<V>> zrevrangebyscore(K key, double max, double min, long offset, long count) {
return zrevrangebyscore(key, string(max), string(min), offset, count);
}
public Future<List<V>> zrevrangebyscore(K key, String max, String min, long offset, long count) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(max).add(min).add(LIMIT).add(offset).add(count);
return dispatch(ZREVRANGEBYSCORE, new ValueListOutput<K, V>(codec), args);
}
public Future<List<ScoredValue<V>>> zrevrangebyscoreWithScores(K key, double max, double min) {
return zrevrangebyscoreWithScores(key, string(max), string(min));
}
public Future<List<ScoredValue<V>>> zrevrangebyscoreWithScores(K key, String max, String min) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(max).add(min).add(WITHSCORES);
return dispatch(ZREVRANGEBYSCORE, new ScoredValueListOutput<K, V>(codec), args);
}
public Future<List<ScoredValue<V>>> zrevrangebyscoreWithScores(K key, double max, double min, long offset, long count) {
return zrevrangebyscoreWithScores(key, string(max), string(min), offset, count);
}
public Future<List<ScoredValue<V>>> zrevrangebyscoreWithScores(K key, String max, String min, long offset, long count) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(key).add(max).add(min).add(WITHSCORES).add(LIMIT).add(offset).add(count);
return dispatch(ZREVRANGEBYSCORE, new ScoredValueListOutput<K, V>(codec), args);
}
public Future<Long> zrevrank(K key, V member) {
return dispatch(ZREVRANK, new IntegerOutput<K, V>(codec), key, member);
}
public Future<Double> zscore(K key, V member) {
return dispatch(ZSCORE, new DoubleOutput<K, V>(codec), key, member);
}
public Future<Long> zunionstore(K destination, K... keys) {
return zunionstore(destination, new ZStoreArgs(), keys);
}
public Future<Long> zunionstore(K destination, ZStoreArgs storeArgs, K... keys) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec);
args.addKey(destination).add(keys.length).addKeys(keys);
storeArgs.build(args);
return dispatch(ZUNIONSTORE, new IntegerOutput<K, V>(codec), args);
}
/**
* Wait until commands are complete or the connection timeout is reached.
*
* @param futures Futures to wait for.
*
* @return True if all futures complete in time.
*/
public boolean awaitAll(Future<?>... futures) {
return awaitAll(timeout, unit, futures);
}
/**
* Wait until futures are complete or the supplied timeout is reached.
*
* @param timeout Maximum time to wait for futures to complete.
* @param unit Unit of time for the timeout.
* @param futures Futures to wait for.
*
* @return True if all futures complete in time.
*/
public boolean awaitAll(long timeout, TimeUnit unit, Future<?>... futures) {
boolean complete;
try {
long nanos = unit.toNanos(timeout);
long time = System.nanoTime();
for (Future<?> f : futures) {
if (nanos < 0) return false;
f.get(nanos, TimeUnit.NANOSECONDS);
long now = System.nanoTime();
nanos -= now - time;
time = now;
}
complete = true;
} catch (TimeoutException e) {
complete = false;
} catch (Exception e) {
throw new RedisCommandInterruptedException(e);
}
return complete;
}
/**
* Close the connection.
*/
public synchronized void close() {
if (!closed && channel != null) {
ConnectionWatchdog watchdog = channel.getPipeline().get(ConnectionWatchdog.class);
watchdog.setReconnect(false);
closed = true;
channel.close();
}
}
public String digest(V script) {
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
md.update(codec.encodeValue(script));
return new String(Base16.encode(md.digest(), false));
} catch (NoSuchAlgorithmException e) {
throw new RedisException("JVM does not support SHA1");
}
}
@Override
public synchronized void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
channel = ctx.getChannel();
List<Command<K, V, ?>> tmp = new ArrayList<Command<K, V, ?>>(queue.size() + 2);
if (password != null) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(password);
tmp.add(new Command<K, V, String>(AUTH, new StatusOutput<K, V>(codec), args, false));
}
if (db != 0) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).add(db);
tmp.add(new Command<K, V, String>(SELECT, new StatusOutput<K, V>(codec), args, false));
}
tmp.addAll(queue);
queue.clear();
for (Command<K, V, ?> cmd : tmp) {
if (!cmd.isCancelled()) {
queue.add(cmd);
channel.write(cmd);
}
}
tmp.clear();
}
@Override
public synchronized void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
if (closed) {
for (Command<K, V, ?> cmd : queue) {
cmd.getOutput().setError("Connection closed");
cmd.complete();
}
queue.clear();
queue = null;
channel = null;
}
}
public <T> Command<K, V, T> dispatch(CommandType type, CommandOutput<K, V, T> output) {
return dispatch(type, output, (CommandArgs<K, V>) null);
}
public <T> Command<K, V, T> dispatch(CommandType type, CommandOutput<K, V, T> output, K key) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key);
return dispatch(type, output, args);
}
public <T> Command<K, V, T> dispatch(CommandType type, CommandOutput<K, V, T> output, K key, V value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addValue(value);
return dispatch(type, output, args);
}
public <T> Command<K, V, T> dispatch(CommandType type, CommandOutput<K, V, T> output, K key, V[] values) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addValues(values);
return dispatch(type, output, args);
}
public synchronized <T> Command<K, V, T> dispatch(CommandType type, CommandOutput<K, V, T> output, CommandArgs<K, V> args) {
Command<K, V, T> cmd = new Command<K, V, T>(type, output, args, multi != null);
try {
if (multi != null) {
multi.add(cmd);
}
queue.put(cmd);
if (channel != null) {
channel.write(cmd);
}
} catch (NullPointerException e) {
throw new RedisException("Connection is closed");
} catch (InterruptedException e) {
throw new RedisCommandInterruptedException(e);
}
return cmd;
}
public <T> T await(Command<K, V, T> cmd, long timeout, TimeUnit unit) {
if (!cmd.await(timeout, unit)) {
cmd.cancel(true);
throw new RedisException("Command timed out");
}
CommandOutput<K, V, T> output = cmd.getOutput();
if (output.hasError()) throw new RedisException(output.getError());
return output.get();
}
@SuppressWarnings("unchecked")
protected <K, V, T> CommandOutput<K, V, T> newScriptOutput(RedisCodec<K, V> codec, ScriptOutputType type) {
switch (type) {
case BOOLEAN: return (CommandOutput<K, V, T>) new BooleanOutput<K, V>(codec);
case INTEGER: return (CommandOutput<K, V, T>) new IntegerOutput<K, V>(codec);
case STATUS: return (CommandOutput<K, V, T>) new StatusOutput<K, V>(codec);
case MULTI: return (CommandOutput<K, V, T>) new NestedMultiOutput<K, V>(codec);
case VALUE: return (CommandOutput<K, V, T>) new ValueOutput<K, V>(codec);
default: throw new RedisException("Unsupported script output type");
}
}
public String string(double n) {
if (Double.isInfinite(n)) {
return (n > 0) ? "+inf" : "-inf";
}
return Double.toString(n);
}
}