package org.commoncrawl.util.redis;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.LinkedList;
import org.commoncrawl.util.ByteArrayUtils;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
/**
* Redis Cmd Wrapper
*
* @author rana
*
*/
public class RedisCmd {
public enum Commands {
// 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,
// 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,
// Scripting
EVAL, EVALSHA, SCRIPT,
// Bits
BITCOUNT, BITOP, GETBIT, SETBIT;
public byte[] bytes;
private Commands() {
bytes = name().getBytes(Charsets.US_ASCII);
}
}
byte[] type;
String args[];
LinkedList<RedisCmd> nestedCommands;
int multiWriteCursor = 0;
int multiReadCursor = -1;
RedisResponse response = null;
RedisClientCallback callback = null;
public RedisCmd(Commands type,String...args) {
this.type = type.bytes;
this.args = args;
}
public RedisCmd(byte[] type,String...args) {
this.type = type;
this.args = args;
}
void addChild(RedisCmd cmd) {
if (nestedCommands == null)
nestedCommands = Lists.newLinkedList();
nestedCommands.add(cmd);
}
boolean isMulti() {
return nestedCommands != null;
}
boolean isExec() {
return ByteArrayUtils.compareBytes(Commands.EXEC.bytes, 0, Commands.EXEC.bytes.length,type, 0, type.length) == 0;
}
boolean gotMultiAck() {
return (multiWriteCursor > -1);
}
boolean allChildrenEncoded() {
return (nestedCommands == null || multiWriteCursor == nestedCommands.size());
}
boolean committed() {
return (nestedCommands == null || multiWriteCursor == nestedCommands.size() + 1);
}
void resetTransmissionState() {
multiWriteCursor = -1;
multiReadCursor = -1;
}
boolean waitingForMultiStartACK() {
return multiReadCursor == -1;
}
void incMultiReadCursor() {
multiReadCursor++;
}
void setFailedMultiChildResponse(RedisResponse response) {
nestedCommands.get(multiReadCursor).response = response;
}
boolean waitingForExecResponse() {
return (multiReadCursor == nestedCommands.size());
}
void encode(OutputStream stream)throws IOException {
stream.write('*');
writeInt(stream, 1 + (args != null ? args.length : 0));
stream.write(RedisClient.CRLF);
stream.write('$');
writeInt(stream, type.length);
stream.write(RedisClient.CRLF);
stream.write(type);
stream.write(RedisClient.CRLF);
if (args != null) {
for (String arg : args) {
byte[] bytes = arg.getBytes(Charset.forName("UTF-8"));
stream.write('$');
writeInt(stream, bytes.length);
stream.write(RedisClient.CRLF);
stream.write(bytes);
stream.write(RedisClient.CRLF);
}
}
}
void encodeNextMultiChild(OutputStream stream)throws IOException {
if (nestedCommands != null && multiWriteCursor > -1 && multiWriteCursor < nestedCommands.size()) {
nestedCommands.get(multiWriteCursor++).encode(stream);
}
else {
throw new IOException("Invalid Encode call to MULTI!");
}
}
void commitMulti(OutputStream stream)throws IOException {
if (nestedCommands == null) {
throw new IOException("Attempting to Commit a MULTI with NULL Children!");
}
else {
stream.write('*');
writeInt(stream, 1);
stream.write(RedisClient.CRLF);
stream.write('$');
writeInt(stream, Commands.EXEC.bytes.length);
stream.write(RedisClient.CRLF);
stream.write(Commands.EXEC.bytes);
stream.write(RedisClient.CRLF);
multiWriteCursor++;
}
}
protected static void writeInt(OutputStream stream, int value)throws IOException {
if (value < 10) {
stream.write('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--) {
stream.write(sb.charAt(i));
}
}
@Override
public String toString() {
return toStringInternal(0);
}
String toStringInternal(int level) {
char temp[] = new char[level * 3];
Arrays.fill(temp,' ');
String pad = new String(temp);
StringBuffer sb = new StringBuffer(pad + "Type:" + new String(type));
if (args != null) {
for (String arg : args) {
sb.append(" Arg:" + arg);
}
}
sb.append('\n');
if (nestedCommands != null) {
for (RedisCmd cmd : nestedCommands) {
sb.append(cmd.toStringInternal(level + 1));
}
}
return sb.toString();
}
}