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); } }