package com.quickserverlab.quickcached;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.quickserverlab.quickcached.cache.CacheException;
import com.quickserverlab.quickcached.cache.CacheInterface;
import org.quickserver.net.server.ClientHandler;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
*
* @author Akshathkumar Shetty
*/
public class TextCommandProcessor {
private static final Logger logger = Logger.getLogger(TextCommandProcessor.class.getName());
private static String versionOutput = null;
static {
versionOutput = "VERSION " + QuickCached.version + "\r\n";
}
private CacheInterface cache;
public void setCache(CacheInterface cache) {
this.cache = cache;
}
public void handleTextCommand(ClientHandler handler, String command)
throws SocketTimeoutException, IOException, CacheException {
if (QuickCached.DEBUG) {
logger.log(Level.FINE, "command: {0}", command);
}
if (command.startsWith("get ") || command.startsWith("gets ")) {
handleGetCommands(handler, command);
} else if (command.equals("version")) {
sendResponse(handler, versionOutput);
} else if (command.startsWith("set ") || command.startsWith("add ")
|| command.startsWith("replace ") || command.startsWith("append ")
|| command.startsWith("prepend ") || command.startsWith("cas ")) {
handleStorageCommands(handler, command);
Data data = (Data) handler.getClientData();
if (data.isAllDataIn()) {
processStorageCommands(handler);
return;
} else {
return;
}
} else if (command.startsWith("delete ")) {
handleDeleteCommands(handler, command);
} else if (command.startsWith("flush_all")) {
handleFlushAll(handler, command);
} else if (command.equals("stats")) {
Map stats = CommandHandler.getStats(handler.getServer());
Set keySet = stats.keySet();
Iterator iterator = keySet.iterator();
String key = null;
String value = null;
while (iterator.hasNext()) {
key = (String) iterator.next();
value = (String) stats.get(key);
sendResponse(handler, "STAT " + key + " " + value + "\r\n");
}
sendResponse(handler, "END\r\n");
} else if (command.startsWith("stats ")) {
//TODO
sendResponse(handler, "ERROR\r\n");
} else if (command.equals("quit")) {
handler.closeConnection();
} else if (command.startsWith("incr ") || command.startsWith("decr ")) {
handleIncrDecrCommands(handler, command);
} else if (command.startsWith("touch ")) {
handleTouchCommands(handler, command);
} else {
logger.log(Level.WARNING, "unknown command! {0}", command);
sendResponse(handler, "ERROR\r\n");
}
}
private void handleFlushAll(ClientHandler handler, String command)
throws SocketTimeoutException, IOException, CacheException {
/*
flush_all [exptime] [noreply]\r\n
*/
String cmdData[] = command.split(" ");
String cmd = cmdData[0];
String exptime = null;
if(QuickCached.DEBUG==false) {
logger.log(Level.FINE, "cmd: {0}", new Object[]{cmd});
}
boolean noreplay = false;
if(cmdData[cmdData.length-1].equals("noreply")) {
noreplay = true;
}
if (noreplay==false && cmdData.length >= 2) {
exptime = cmdData[1];
} else if (noreplay==true && cmdData.length >= 3) {
exptime = cmdData[1];
}
if (exptime == null) {
cache.flush();
} else {
final int sleeptime = Integer.parseInt(exptime);
Thread t = new Thread() {
public void run() {
try {
sleep(1000 * sleeptime);
} catch (InterruptedException ex) {
logger.log(Level.WARNING, "Error: "+ex, ex);
}
try {
cache.flush();
} catch (CacheException ex) {
logger.log(Level.SEVERE, "Error: "+ex, ex);
}
}
};
t.start();
}
if (noreplay) {
return;
}
sendResponse(handler, "OK\r\n");
}
private void handleDeleteCommands(ClientHandler handler, String command)
throws SocketTimeoutException, IOException, CacheException {
/*
delete <key> [noreply]\r\n
*/
String cmdData[] = command.split(" ");
String cmd = cmdData[0];
String key = cmdData[1];
if(QuickCached.DEBUG==false) {
logger.log(Level.FINE, "cmd: {0}, key: {1}", new Object[]{cmd, key});
}
boolean noreplay = false;
if (cmdData.length == 3) {
if ("noreply".equals(cmdData[2])) {
noreplay = true;
}
}
boolean flag = cache.delete(key);
if (noreplay) {
return;
}
if (flag == true) {
sendResponse(handler, "DELETED\r\n");
} else {
sendResponse(handler, "NOT_FOUND\r\n");
}
}
private void handleTouchCommands(ClientHandler handler, String command)
throws SocketTimeoutException, IOException, CacheException {
/*
touch <key> <exptime> [noreply]\r\n
*/
String cmdData[] = command.split(" ");
String cmd = cmdData[0];
String key = cmdData[1];
int exptime = Integer.parseInt(cmdData[2]);
boolean noreplay = false;
if (cmdData.length >= 4) {
if ("noreply".equals(cmdData[3])) {
noreplay = true;
}
}
if(QuickCached.DEBUG==false) {
logger.log(Level.FINE, "cmd: {0}, key: {1}", new Object[]{cmd, key});
}
boolean flag = cache.touch(key, exptime);
if(noreplay) return;
if(flag==false) {
sendResponse(handler, "NOT_FOUND\r\n");
} else {
sendResponse(handler, "TOUCHED\r\n");
}
}
private void handleGetCommands(ClientHandler handler, String command)
throws SocketTimeoutException, IOException, CacheException {
/*
get <key>*\r\n
gets <key>*\r\n
*/
String cmdData[] = command.split(" ");
String cmd = cmdData[0];
String key = null;
for (int i = 1; i < cmdData.length; i++) {
key = cmdData[i];
if(QuickCached.DEBUG==false) {
logger.log(Level.FINE, "cmd: {0}, key: {1}", new Object[]{cmd, key});
}
DataCarrier dc = (DataCarrier) cache.get(key);
if (dc != null) {
StringBuilder sb = new StringBuilder();
sb.append("VALUE ");
sb.append(key);
sb.append(" ");
sb.append(dc.getFlags());
sb.append(" ");
sb.append(dc.getData().length);
sb.append(" ");
sb.append(dc.getCas());
sb.append("\r\n");
sendResponse(handler, sb.toString());
sendResponse(handler, dc.getData());
sendResponse(handler, "\r\n");
}
}
sendResponse(handler, "END\r\n");
/*
VALUE <key> <flags> <bytes> [<cas unique>]\r\n
<data block>\r\n
*/
}
private void handleIncrDecrCommands(ClientHandler handler, String command)
throws SocketTimeoutException, IOException, CacheException {
/*
incr <key> <value> [noreply]\r\n
decr <key> <value> [noreply]\r\n
*/
String cmdData[] = command.split(" ");
if (cmdData.length < 3) {
sendResponse(handler, "CLIENT_ERROR Bad number of args passed\r\n");
if (cmdData[0].equals("incr")) {
CommandHandler.incrMisses++;
} else if (cmdData[0].equals("decr")) {
CommandHandler.decrMisses++;
}
return;
}
String cmd = cmdData[0];
String key = cmdData[1];
String _value = cmdData[2];
long value = 0;
try {
value = Long.parseLong(_value);
} catch (Exception e) {
sendResponse(handler, "CLIENT_ERROR parse of client value failed\r\n");
if (cmd.equals("incr")) {
CommandHandler.incrMisses++;
} else if (cmd.equals("decr")) {
CommandHandler.decrMisses++;
}
return;
}
if(QuickCached.DEBUG==false) {
logger.log(Level.FINE, "cmd: {0}, key: {1}", new Object[]{cmd, key});
}
boolean noreplay = false;
if (cmdData.length >= 4) {
if ("noreply".equals(cmdData[3])) {
noreplay = true;
}
}
DataCarrier dc = (DataCarrier) cache.get(key, false);
if (dc == null) {
if (noreplay == false) {
sendResponse(handler, "NOT_FOUND\r\n");
}
if (cmd.equals("incr")) {
CommandHandler.incrMisses++;
} else if (cmd.equals("decr")) {
CommandHandler.decrMisses++;
}
return;
}
StringBuilder sb = new StringBuilder();
dc.writeLock.lock();
try {
long oldvalue = Long.parseLong(new String(dc.getData(),
HexUtil.getCharset()));
if (cmd.equals("incr")) {
value = oldvalue + value;
} else if (cmd.equals("decr")) {
value = oldvalue - value;
if (value < 0) {
value = 0;
}
} else {
throw new IllegalArgumentException("Unknown command "+cmd);
}
sb.append(value);
dc.setData(sb.toString().getBytes(HexUtil.getCharset()));
cache.update(key, dc, dc.getSize());
} catch(Exception e) {
if(noreplay == false) {
sendResponse(handler, "CLIENT_ERROR parse of server value failed\r\n");
}
if (cmd.equals("incr")) {
CommandHandler.incrMisses++;
} else if (cmd.equals("decr")) {
CommandHandler.decrMisses++;
}
return;
} finally {
dc.writeLock.unlock();
}
if (cmd.equals("incr")) {
CommandHandler.incrHits++;
} else if (cmd.equals("decr")) {
CommandHandler.decrHits++;
}
if (noreplay) {
return;
}
sb.append("\r\n");
sendResponse(handler, sb.toString());
}
private void handleStorageCommands(ClientHandler handler, String command)
throws SocketTimeoutException, IOException {
Data data = (Data) handler.getClientData();
/*
<command name> <key> <flags> <exptime> <bytes> [noreply]\r\n
cas <key> <flags> <exptime> <bytes> <cas unique> [noreply]\r\n
*/
String cmdData[] = command.split(" ");
String cmd = cmdData[0];
String key = cmdData[1];
String flags = cmdData[2];
int exptime = Integer.parseInt(cmdData[3]);
long bytes = Integer.parseInt(cmdData[4]);
String casunique = null;
boolean noreplay = false;
if (cmdData.length >= 6) {
if ("noreply".equals(cmdData[5])) {
noreplay = true;
} else {
casunique = cmdData[5];
}
if (cmdData.length >= 7) {
if ("noreply".equals(cmdData[6])) {
noreplay = true;
}
}
}
if(key.length()>Data.getMaxSizeAllowedForKey()) {
throw new IllegalArgumentException(
"key passed to big to store "+key);
}
if(Data.getMaxSizeAllowedForValue()>0) {
if(bytes > Data.getMaxSizeAllowedForValue()) {
throw new IllegalArgumentException(
"value passed to big to store "+bytes+" for key "+key);
}
}
data.setCmd(cmd);
data.setKey(key);
data.setFlags(flags);
data.setExptime(exptime);
data.setDataRequiredLength(bytes);
data.setCasUnique(casunique);
data.setNoreplay(noreplay);
}
public void processStorageCommands(ClientHandler handler)
throws SocketTimeoutException, IOException, CacheException {
Data data = (Data) handler.getClientData();
if(QuickCached.DEBUG==false) {
logger.log(Level.FINE, "cmd: {0}, key: {1}", new Object[]{data.getCmd(), data.getKey()});
}
byte dataToStore[] = data.getDataByte();
DataCarrier dc = new DataCarrier(dataToStore);
dc.setFlags(data.getFlags());
if (data.getCmd().equals("set")) {
DataCarrier olddata = (DataCarrier) cache.get(data.getKey(), false);
if(olddata==null) {
cache.set(data.getKey(), dc, dc.getSize(), data.getExptime());
} else {
olddata.writeLock.lock();
try {
olddata.setData(dc.getData());
olddata.setFlags(dc.getFlags());
cache.update(data.getKey(), olddata, olddata.getSize(), data.getExptime());
} finally {
olddata.writeLock.unlock();
}
}
if (data.isNoreplay() == false) {
sendResponse(handler, "STORED\r\n");
}
} else if (data.getCmd().equals("add")) {
Object olddata = cache.get(data.getKey(), false);
if (olddata == null) {
cache.set(data.getKey(), dc, dc.getSize(), data.getExptime());
if (data.isNoreplay() == false) {
sendResponse(handler, "STORED\r\n");
}
} else {
if (data.isNoreplay() == false) {
sendResponse(handler, "NOT_STORED\r\n");
}
}
} else if (data.getCmd().equals("replace")) {
DataCarrier olddata = (DataCarrier) cache.get(data.getKey(), false);
if (olddata != null) {
olddata.writeLock.lock();
try {
olddata.setData(dc.getData());
cache.update(data.getKey(), olddata, olddata.getSize());
} finally {
olddata.writeLock.unlock();
}
dc.setData(null);
dc = null;
if (data.isNoreplay() == false) {
if (data.isNoreplay() == false) {
sendResponse(handler, "STORED\r\n");
}
}
} else {
if (data.isNoreplay() == false) {
sendResponse(handler, "NOT_STORED\r\n");
}
}
} else if (data.getCmd().equals("append")) {
DataCarrier olddata = (DataCarrier) cache.get(data.getKey(), false);
if (olddata != null) {
olddata.writeLock.lock();
try {
olddata.append(dc.getData());
cache.update(data.getKey(), olddata, olddata.getSize());
} finally {
olddata.writeLock.unlock();
}
dc.setData(null);
dc = null;
if (data.isNoreplay() == false) {
if (data.isNoreplay() == false) {
sendResponse(handler, "STORED\r\n");
}
}
} else {
if (data.isNoreplay() == false) {
sendResponse(handler, "NOT_STORED\r\n");
}
}
} else if (data.getCmd().equals("prepend")) {
DataCarrier olddata = (DataCarrier) cache.get(data.getKey(), false);
if (olddata != null) {
olddata.writeLock.lock();
try {
olddata.prepend(dc.getData());
cache.update(data.getKey(), olddata, olddata.getSize());
} finally {
olddata.writeLock.unlock();
}
dc.setData(null);
dc = null;
if (data.isNoreplay() == false) {
if (data.isNoreplay() == false) {
sendResponse(handler, "STORED\r\n");
}
}
} else {
if (data.isNoreplay() == false) {
sendResponse(handler, "NOT_STORED\r\n");
}
}
} else if (data.getCmd().equals("cas")) {
String reply = null;
DataCarrier olddata = (DataCarrier) cache.get(data.getKey(), false);
if(olddata != null) {
olddata.writeLock.lock();
try {
int oldcas = olddata.getCas();
int passedcas = Integer.parseInt(data.getCasUnique());
if (oldcas == passedcas) {
olddata.setData(dc.getData());
cache.update(data.getKey(), olddata, olddata.getSize());
dc.setData(null);
dc = null;
CommandHandler.casHits++;
if (data.isNoreplay() == false) {
reply = "STORED\r\n";
}
} else {
CommandHandler.casBadval++;
if (data.isNoreplay() == false) {
reply = "EXISTS\r\n";
}
}
} finally {
olddata.writeLock.unlock();
}
} else {
CommandHandler.casMisses++;
if (data.isNoreplay() == false) {
reply = "NOT_FOUND\r\n";
}
}
if(reply!=null) {
sendResponse(handler, reply);
}
}
data.clear();
}
public void sendResponse(ClientHandler handler, String data) throws SocketTimeoutException, IOException {
sendResponse(handler, data.getBytes(HexUtil.getCharset()));
}
public void sendResponse(ClientHandler handler, byte data[]) throws SocketTimeoutException, IOException {
if(handler.getCommunicationLogging() || QuickCached.DEBUG) {
logger.log(Level.FINE, "S: {0}", new String(data, HexUtil.getCharset()));
} else {
logger.log(Level.FINE, "S: {0} bytes", data.length);
}
handler.sendClientBinary(data);
}
}